* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef DISTRIBUTED_POINT_FUNCTIONS_DPF_INTERNAL_INT_MOD_N_H_
#define DISTRIBUTED_POINT_FUNCTIONS_DPF_INTERNAL_INT_MOD_N_H_
#include <glog/logging.h>
#include "absl/container/inlined_vector.h"
#include "absl/numeric/int128.h"
#include "absl/status/statusor.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
namespace distributed_point_functions {
namespace dpf_internal {
class IntModNBase {
public:
static double GetSecurityLevel(int num_samples, absl::uint128 modulus);
static absl::Status CheckParameters(int num_samples, int base_integer_bitsize,
absl::uint128 modulus,
double security_parameter);
static absl::StatusOr<int> GetNumBytesRequired(int num_samples,
int base_integer_bitsize,
absl::uint128 modulus,
double security_parameter);
template <typename T>
static T ConvertBytesTo(absl::string_view bytes) {
CHECK(bytes.size() == sizeof(T));
T out{0};
#ifdef ABSL_IS_LITTLE_ENDIAN
std::copy_n(bytes.begin(), sizeof(T), reinterpret_cast<char*>(&out));
#else
for (int i = sizeof(T) - 1; i >= 0; --i) {
out |= absl::bit_cast<uint8_t>(bytes[i]);
out <<= 8;
}
#endif
return out;
}
};
template <typename BaseInteger, typename ModulusType, ModulusType kModulus>
class IntModNImpl : public IntModNBase {
static_assert(sizeof(BaseInteger) <= sizeof(absl::uint128),
"BaseInteger may be at most 128 bits large");
static_assert(
std::is_same<BaseInteger, absl::uint128>::value ||
#ifdef ABSL_HAVE_INTRINSIC_INT128
std::is_same<BaseInteger, unsigned __int128>::value ||
#endif
std::is_unsigned<BaseInteger>::value,
"BaseInteger must be unsigned");
static_assert(kModulus <= ModulusType(BaseInteger(-1)),
"kModulus must fit in BaseInteger");
public:
using Base = BaseInteger;
constexpr IntModNImpl() : value_(0) {}
explicit constexpr IntModNImpl(BaseInteger value)
: value_(value % kModulus) {}
constexpr IntModNImpl(const IntModNImpl& a) = default;
constexpr IntModNImpl& operator=(const IntModNImpl& a) = default;
constexpr IntModNImpl& operator=(const BaseInteger& a) {
value_ = a % kModulus;
return *this;
}
constexpr IntModNImpl& operator+=(const IntModNImpl& a) {
AddBaseInteger(a.value_);
return *this;
}
constexpr IntModNImpl& operator-=(const IntModNImpl& a) {
SubtractBaseInteger(a.value_);
return *this;
}
constexpr BaseInteger value() const { return value_; }
static constexpr BaseInteger modulus() { return kModulus; }
static absl::StatusOr<int> GetNumBytesRequired(int num_samples,
double security_parameter) {
return IntModNBase::GetNumBytesRequired(
num_samples, 8 * sizeof(BaseInteger), kModulus, security_parameter);
}
template <int kCompiledNumSamples = 1>
static void UnsafeSampleFromBytes(absl::string_view bytes,
double security_parameter,
absl::Span<IntModNImpl> samples) {
static_assert(kCompiledNumSamples >= 1,
"kCompiledNumSamples must be positive");
absl::uint128 r = ConvertBytesTo<absl::uint128>(bytes.substr(0, 16));
absl::InlinedVector<BaseInteger, std::max(1, kCompiledNumSamples - 1)>
randomness(samples.size() - 1);
for (int i = 0; i < static_cast<int>(randomness.size()); ++i) {
randomness[i] = ConvertBytesTo<BaseInteger>(
bytes.substr(16 + i * sizeof(BaseInteger), sizeof(BaseInteger)));
}
for (int i = 0; i < static_cast<int>(samples.size()); ++i) {
samples[i] = IntModNImpl(static_cast<BaseInteger>(r % kModulus));
if (i < static_cast<int>(randomness.size())) {
r /= kModulus;
if (sizeof(BaseInteger) < sizeof(absl::uint128)) {
r <<= (sizeof(BaseInteger) * 8);
}
r |= randomness[i];
}
}
}
static absl::Status SampleFromBytes(absl::string_view bytes,
double security_parameter,
absl::Span<IntModNImpl> samples) {
if (samples.empty()) {
return absl::InvalidArgumentError(
"The number of samples required must be > 0");
}
absl::StatusOr<int> num_bytes_lower_bound =
GetNumBytesRequired(samples.size(), security_parameter);
if (!num_bytes_lower_bound.ok()) {
return num_bytes_lower_bound.status();
}
if (*num_bytes_lower_bound > bytes.size()) {
return absl::InvalidArgumentError(
absl::StrCat("The number of bytes provided (", bytes.size(),
") is insufficient for the required "
"statistical security and number of samples."));
}
UnsafeSampleFromBytes(bytes, security_parameter, samples);
return absl::OkStatus();
}
private:
constexpr void SubtractBaseInteger(const BaseInteger& a) {
if (value_ >= a) {
value_ -= a;
} else {
value_ = kModulus - a + value_;
}
}
constexpr void AddBaseInteger(const BaseInteger& a) {
SubtractBaseInteger(kModulus - a);
}
BaseInteger value_;
};
template <typename BaseInteger, typename ModulusType, ModulusType kModulus>
constexpr IntModNImpl<BaseInteger, ModulusType, kModulus> operator+(
IntModNImpl<BaseInteger, ModulusType, kModulus> a,
const IntModNImpl<BaseInteger, ModulusType, kModulus>& b) {
a += b;
return a;
}
template <typename BaseInteger, typename ModulusType, ModulusType kModulus>
constexpr IntModNImpl<BaseInteger, ModulusType, kModulus> operator-(
IntModNImpl<BaseInteger, ModulusType, kModulus> a,
const IntModNImpl<BaseInteger, ModulusType, kModulus>& b) {
a -= b;
return a;
}
template <typename BaseInteger, typename ModulusType, ModulusType kModulus>
constexpr IntModNImpl<BaseInteger, ModulusType, kModulus> operator-(
IntModNImpl<BaseInteger, ModulusType, kModulus> a) {
IntModNImpl<BaseInteger, ModulusType, kModulus> result(BaseInteger{0});
result -= a;
return result;
}
template <typename BaseInteger, typename ModulusType, ModulusType kModulus>
constexpr bool operator==(
const IntModNImpl<BaseInteger, ModulusType, kModulus>& a,
const IntModNImpl<BaseInteger, ModulusType, kModulus>& b) {
return a.value() == b.value();
}
template <typename BaseInteger, typename ModulusType, ModulusType kModulus>
constexpr bool operator!=(
const IntModNImpl<BaseInteger, ModulusType, kModulus>& a,
const IntModNImpl<BaseInteger, ModulusType, kModulus>& b) {
return !(a == b);
}
}
#ifdef ABSL_HAVE_INTRINSIC_INT128
template <typename BaseInteger, unsigned __int128 kModulus>
using IntModN =
dpf_internal::IntModNImpl<BaseInteger, unsigned __int128, kModulus>;
#else
template <typename BaseInteger, BaseInteger kModulus>
using IntModN = dpf_internal::IntModNImpl<BaseInteger, BaseInteger, kModulus>;
#endif
}
#endif