Skip to content

Commit

Permalink
Merge pull request #3 from itzmeanjan/poly-matrix
Browse files Browse the repository at this point in the history
Matrix/ Vector of Polynomials
  • Loading branch information
itzmeanjan committed Jul 18, 2023
2 parents ffce6dd + b38ddbd commit 1872701
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 5 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test.yml → .github/workflows/test_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Test Saber KEM using CI

on:
push:
branches: [ "master" ]
branches: [ "main" ]
pull_request:
branches: [ "master" ]
branches: [ "main" ]

jobs:
build:
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ all: test
tests/test_polynomial.o: tests/test_polynomial.cpp include/*.hpp
$(CXX) $(CXX_FLAGS) $(WARN_FLAGS) $(OPT_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) -c $< -o $@

tests/a.out: tests/test_polynomial.o
tests/test_poly_matrix.o: tests/test_poly_matrix.cpp include/*.hpp
$(CXX) $(CXX_FLAGS) $(WARN_FLAGS) $(OPT_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) -c $< -o $@

tests/a.out: tests/test_polynomial.o tests/test_poly_matrix.o
$(CXX) $(OPT_FLAGS) $^ -lgtest -lgtest_main -o $@

test: tests/a.out
Expand Down
184 changes: 184 additions & 0 deletions include/poly_matrix.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#pragma once
#include "params.hpp"
#include "polynomial.hpp"
#include "shake128.hpp"
#include <array>
#include <span>

// Operations defined over matrix/ vector of polynomials.
namespace poly_matrix {

// Wrapper type encapsulating matrix/ vector operations s.t. its elements are
// polynomials in Rq = Zq[X]/(X^N + 1), N = 256.
template<const size_t rows, const size_t cols, const uint16_t moduli>
struct poly_matrix_t
{
private:
std::array<polynomial::poly_t<moduli>, rows * cols> elements{};

public:
// Constructors
inline constexpr poly_matrix_t() = default;
inline constexpr poly_matrix_t(
std::array<polynomial::poly_t<moduli>, rows * cols> arr)
{
elements = arr;
}

// Given a byte array of length rows * log2(moduli) * 32 -bytes, this routine
// can be used for transforming it into a vector of polynomials, following
// algorithm 11 of spec.
inline explicit poly_matrix_t(std::span<const uint8_t> bstr)
requires(cols == 1)
{
constexpr size_t poly_blen = polynomial::N * saber_params::log2(moduli) / 8;
for (size_t i = 0; i < rows; i++) {
polynomial::poly_t<moduli> poly(bstr.subspan(i * poly_blen, poly_blen));
elements[i] = poly;
}
}

// Given a vector of polynomials, this routine can transform it into a byte
// string of length rows * log2(moduli) * 32, following algorithm 12 of spec.
inline void to_bytes(std::span<uint8_t> bstr)
requires(cols == 1)
{
constexpr size_t poly_blen = polynomial::N * saber_params::log2(moduli) / 8;
for (size_t i = 0; i < rows; i++) {
elements[i].to_bytes(bstr.subspan(i * poly_blen, poly_blen));
}
}

// Given a matrix M ∈ Rq^(l×l) and vector v ∈ Rq^(l×1), this routine performs
// a matrix vector multiplication, returning a vector mv ∈ Rq^(l×1), following
// algorithm 13 of spec.
template<const size_t rhs_rows>
inline poly_matrix_t<rows, 1, moduli> mat_vec_mul(
const poly_matrix_t<rhs_rows, 1, moduli>& vec)
requires((rows == cols) && (cols == rhs_rows))
{
poly_matrix_t<rows, 1, moduli> res;

auto mat = this;
for (size_t i = 0; i < rows; i++) {
polynomial::poly_t<moduli> poly;

for (size_t j = 0; j < cols; j++) {
poly += (mat.elements[i * cols + j] * vec.elements[j]);
}
res[i] = poly;
}

return res;
}

// Given two vectors v_a, v_b ∈ Rp^(l×1), this routine computes their inner
// product, returning a polynomial c ∈ Rp, following algorithm 14 of spec.
inline polynomial::poly_t<moduli> inner_prod(
const poly_matrix_t<rows, cols, moduli>& vec)
requires(cols == 1)
{
polynomial::poly_t<moduli> res;

for (size_t i = 0; i < rows; i++) {
res += (this->elements[i] * vec.elements[i]);
}

return res;
}

// Given random byte string ( seed ) of length `seedbytes` as input,
// this routine generates a matrix A ∈ Rq^(l×l), following algorithm 15 of
// spec.
template<const size_t seedbytes>
inline static poly_matrix_t<rows, cols, moduli> gen_matrix(
std::span<const uint8_t, seedbytes> seed)
requires(rows == cols)
{
constexpr size_t ϵ = saber_params::log2(moduli);
constexpr size_t poly_blen = (polynomial::N * ϵ) / 8;
constexpr size_t buf_blen = rows * cols * poly_blen;

poly_matrix_t<rows, cols, moduli> mat;

std::array<uint8_t, buf_blen> buf{};
auto bufs = std::span<uint8_t, buf_blen>(buf);

shake128::shake128 hasher;
hasher.absorb(seed.data(), seed.size());
hasher.finalize();
hasher.squeeze(buf.data(), buf.size());
hasher.reset();

for (size_t i = 0; i < rows * cols; i++) {
auto bstr = bufs.subspan(i * poly_blen, poly_blen);
polynomial::poly_t<moduli> poly(bstr);
mat.elements[i] = poly;
}

return mat;
}

// Given random byte string ( seed ) of length `noise_seedbytes` as input,
// this routine outputs a secret vector v ∈ Rq^(l×1) with its coefficients
// sampled from a centered binomial distribution β_μ.
template<const size_t noise_seedbytes>
inline static poly_matrix_t<rows, 1, moduli> gen_secret(
std::span<const uint8_t, noise_seedbytes> seed)
requires(cols == 1)
{
constexpr size_t μ = saber_params::log2(moduli);
constexpr uint16_t q = 1u << (μ / 2);
constexpr size_t poly_blen = (polynomial::N * μ) / 8;
constexpr size_t buf_blen = rows * poly_blen;

poly_matrix_t<rows, 1, moduli> vec;

std::array<uint8_t, buf_blen> buf{};
auto bufs = std::span<uint8_t, buf_blen>(buf);

shake128::shake128 hasher;
hasher.absorb(seed.data(), seed.size());
hasher.finalize();
hasher.squeeze(buf.data(), buf.size());
hasher.reset();

for (size_t i = 0; i < rows; i++) {
size_t off = i * poly_blen;

auto bstr_a = bufs.subspan(off, poly_blen / 2);
polynomial::poly_t<q> poly_a(bstr_a);

size_t j = 0, k = 0;
while (j < polynomial::N / 2) {
const auto hw0 = poly_a[k].template hamming_weight<q>();
const auto hw1 = poly_a[k + 1].template hamming_weight<q>();

vec.elements[i][j] = hw0 - hw1;

j += 1;
k += 2;
}

off += bstr_a.size();

auto bstr_b = bufs.subspan(off, poly_blen / 2);
polynomial::poly_t<q> poly_b(bstr_b);

k = 0;
while (j < polynomial::N) {
const auto hw0 = poly_b[k].template hamming_weight<q>();
const auto hw1 = poly_b[k + 1].template hamming_weight<q>();

vec.elements[i][j] = hw0 - hw1;

j += 1;
k += 2;
}
}

return vec;
}
};

}
8 changes: 7 additions & 1 deletion include/polynomial.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ struct poly_t
coeffs = res;
}

// Returns coefficient at given polynomial index ∈ [0, N).
inline constexpr zq::zq_t operator[](const size_t idx) const
{
return coeffs[idx];
}

// Addition of two polynomials s.t. their coefficients are over Zq.
inline constexpr poly_t operator+(const poly_t& rhs) const
{
Expand All @@ -153,7 +159,7 @@ struct poly_t
inline constexpr void operator+=(const poly_t& rhs) { *this = *this + rhs; }

// Multiplication of two polynomials s.t. their coefficients are over Zq.
inline constexpr void operator*(const poly_t& rhs) const
inline constexpr poly_t operator*(const poly_t& rhs) const
{
return karatsuba::karamul(this->coeffs, rhs.coeffs);
}
Expand Down
10 changes: 10 additions & 0 deletions include/zq.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include "params.hpp"
#include <bit>
#include <cstddef>

// Arithmetic operations over Zq s.t. q = 2^i, i >= 0
Expand Down Expand Up @@ -62,6 +63,15 @@ struct zq_t

// Raw value ∈ Zq
inline constexpr uint16_t as_raw() const { return this->val; }

// Returns hamming weight (i.e. number of non-zero bits present) of bit string
// representation of Zq element.
template<const size_t moduli>
inline constexpr size_t hamming_weight() const
requires(saber_params::is_power_of_2(moduli))
{
return std::popcount(reduce_by<moduli>().as_raw());
}
};

}
41 changes: 41 additions & 0 deletions tests/test_poly_matrix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "poly_matrix.hpp"
#include "prng.hpp"
#include <gtest/gtest.h>
#include <vector>

// Ensure functional correctness of implementation of data conversion algorithms
// i.e. algorithms used for transforming in between byte strings and vector of
// degree-255 polynomials.
//
// `moduli` must be a power of 2 value.
template<const size_t rows, const uint16_t moduli>
void
test_poly_matrix_conversion()
{
constexpr size_t pblen = (saber_params::log2(moduli) * polynomial::N) / 8;
constexpr size_t vblen = rows * pblen;

std::vector<uint8_t> src_bstr(vblen, 0);
std::vector<uint8_t> dst_bstr(vblen, 0);

prng::prng_t prng;
prng.read(src_bstr);

poly_matrix::poly_matrix_t<rows, 1, moduli> pmat(src_bstr);
pmat.to_bytes(dst_bstr);

ASSERT_EQ(src_bstr, dst_bstr);
}

TEST(SaberKEM, PolynomialMatrixConversion)
{
test_poly_matrix_conversion<2, (1 << 3)>();
test_poly_matrix_conversion<3, (1 << 4)>();
test_poly_matrix_conversion<4, (1 << 6)>();
test_poly_matrix_conversion<2, (1 << 10)>();
test_poly_matrix_conversion<3, (1 << 10)>();
test_poly_matrix_conversion<4, (1 << 10)>();
test_poly_matrix_conversion<2, (1 << 13)>();
test_poly_matrix_conversion<3, (1 << 13)>();
test_poly_matrix_conversion<4, (1 << 13)>();
}
2 changes: 1 addition & 1 deletion tests/test_polynomial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
// `moduli` must be a power of 2 value.
template<const uint16_t moduli>
inline void
void
test_poly_conversion()
{
constexpr size_t blen = (saber_params::log2(moduli) * polynomial::N) / 8;
Expand Down

0 comments on commit 1872701

Please sign in to comment.