Intel HE Acceleration Library for FPGAs
Intel Homomorphic Encryption Acceleration Library for FPGAs, accelerating the modular arithmetic operations used in homomorphic encryption on Intel FPGAs.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
number_theory_util.h
Go to the documentation of this file.
1 // Copyright (C) 2020-2021 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 
4 #ifndef __NUMBER_THEORY_H__
5 #define __NUMBER_THEORY_H__
6 
7 #include <cstdint>
8 #include <cmath>
9 #include <iostream>
10 #include <limits>
11 #include <vector>
12 
13 #include "fpga_assert.h"
14 
15 namespace intel {
16 namespace hexl {
17 namespace fpga {
18 
19 __extension__ typedef unsigned __int128 uint128_t;
20 
21 // Returns whether or not num is a power of two
22 inline bool IsPowerOfTwo(uint64_t num) { return num && !(num & (num - 1)); }
23 
24 // Returns maximum number of possible significant bits given modulus
25 inline uint64_t MSB(uint64_t modulus) {
26  return static_cast<uint64_t>(std::log2l(modulus));
27 }
28 
29 // Returns log2(x) for x a power of 2
30 inline uint64_t Log2(uint64_t x) {
31  FPGA_ASSERT(IsPowerOfTwo(x), "x not a power of 2");
32  return MSB(x);
33 }
34 
35 // Returns the maximum value that can be represented using bits bits
36 inline uint64_t MaximumValue(uint64_t bits) {
37  FPGA_ASSERT(bits <= 64, "MaximumValue requires bits <= 64");
38  if (bits == 64) {
39  return (std::numeric_limits<uint64_t>::max)();
40  }
41  return (1ULL << bits) - 1;
42 }
43 
44 // Return x * y as 128-bit integer
45 // Correctness if x * y < 128 bits
46 inline uint128_t MultiplyUInt64(uint64_t x, uint64_t y) {
47  return uint128_t(x) * uint128_t(y);
48 }
49 
50 // Multiplies x * y as 128-bit integer.
51 // @param prod_hi Stores high 64 bits of product
52 // @param prod_lo Stores low 64 bits of product
53 inline void MultiplyUInt64(uint64_t x, uint64_t y, uint64_t* prod_hi,
54  uint64_t* prod_lo) {
55  uint128_t prod = MultiplyUInt64(x, y);
56  *prod_hi = static_cast<uint64_t>(prod >> 64);
57  *prod_lo = static_cast<uint64_t>(prod);
58 }
59 
60 // Return the high 128 minus BitShift bits of the 128-bit product x * y
61 template <int BitShift>
62 inline uint64_t MultiplyUInt64Hi(uint64_t x, uint64_t y) {
63  uint128_t product = MultiplyUInt64(x, y);
64  return static_cast<uint64_t>(product >> BitShift);
65 }
66 
67 // Returns low 64bit of 128b/64b where x1=high 64b, x0=low 64b
68 inline uint64_t DivideUInt128UInt64Lo(uint64_t x1, uint64_t x0, uint64_t y) {
69  uint128_t n =
70  (static_cast<uint128_t>(x1) << 64) | (static_cast<uint128_t>(x0));
71  uint128_t q = n / y;
72 
73  return static_cast<uint64_t>(q);
74 }
75 
76 // Computes (x * y) mod modulus, except that the output is in [0, 2 * modulus]
77 // @param modulus_precon Pre-computed Barrett reduction factor
78 template <int BitShift>
79 inline uint64_t MultiplyUIntModLazy(uint64_t x, uint64_t y_operand,
80  uint64_t y_barrett_factor,
81  uint64_t modulus) {
82  FPGA_ASSERT(y_operand < modulus, "y_operand must be less than modulus");
83  FPGA_ASSERT(modulus <= MaximumValue(BitShift), "Modulus exceeds bound");
84  FPGA_ASSERT(x <= MaximumValue(BitShift), "x exceeds bound");
85 
86  uint64_t Q = MultiplyUInt64Hi<BitShift>(x, y_barrett_factor);
87  return y_operand * x - Q * modulus;
88 }
89 
90 // Computes (x * y) mod modulus, except that the output is in [0, 2 * modulus]
91 template <int BitShift>
92 inline uint64_t MultiplyUIntModLazy(uint64_t x, uint64_t y, uint64_t modulus) {
93  FPGA_ASSERT(BitShift == 64 || BitShift == 52, "Unsupport BitShift");
94  FPGA_ASSERT(x <= MaximumValue(BitShift), "x exceeds bound");
95  FPGA_ASSERT(y < modulus, "y must be less than modulus");
96  FPGA_ASSERT(modulus <= MaximumValue(BitShift), "modulus exceeds bound");
97  uint64_t y_hi{0};
98  uint64_t y_lo{0};
99  if (BitShift == 64) {
100  y_hi = y;
101  y_lo = 0;
102  } else if (BitShift == 52) {
103  y_hi = y >> 12;
104  y_lo = y << 52;
105  }
106  uint64_t y_barrett = DivideUInt128UInt64Lo(y_hi, y_lo, modulus);
107  return MultiplyUIntModLazy<BitShift>(x, y, y_barrett, modulus);
108 }
109 
110 // Adds two unsigned 64-bit integers
111 // @param operand1 Number to add
112 // @param operand2 Number to add
113 // @param result Stores the sum
114 // @return The carry bit
115 inline unsigned char AddUInt64(uint64_t operand1, uint64_t operand2,
116  uint64_t* result) {
117  *result = operand1 + operand2;
118  return static_cast<unsigned char>(*result < operand1);
119 }
120 
121 // Returns whether or not the input is prime
122 bool IsPrime(uint64_t n);
123 
124 // Generates a list of num_primes primes in the range [2^(bit_size,
125 // 2^(bit_size+1)]. Ensures each prime q satisfies
126 // q % (2*ntt_size+1)) == 1
127 // @param num_primes Number of primes to generate
128 // @param bit_size Bit size of each prime
129 // @param ntt_size N such that each prime q satisfies q % (2N) == 1. N must be
130 // a power of two
131 std::vector<uint64_t> GeneratePrimes(size_t num_primes, size_t bit_size,
132  size_t ntt_size = 1);
133 
134 // returns input mod modulus, computed via Barrett reduction
135 // @param q_barr floor(2^64 / p)
136 uint64_t BarrettReduce64(uint64_t input, uint64_t modulus, uint64_t q_barr);
137 
138 template <int InputModFactor>
139 uint64_t ReduceMod(uint64_t x, uint64_t modulus,
140  const uint64_t* twice_modulus = nullptr,
141  const uint64_t* four_times_modulus = nullptr) {
142  FPGA_ASSERT(InputModFactor == 1 || InputModFactor == 2 ||
143  InputModFactor == 4 || InputModFactor == 8,
144  "InputModFactor should be 1, 2, 4, or 8");
145  if (InputModFactor == 1) {
146  return x;
147  }
148  if (InputModFactor == 2) {
149  if (x >= modulus) {
150  x -= modulus;
151  }
152  return x;
153  }
154  if (InputModFactor == 4) {
155  FPGA_ASSERT(twice_modulus != nullptr,
156  "twice_modulus should not be nullptr");
157  if (x >= *twice_modulus) {
158  x -= *twice_modulus;
159  }
160  if (x >= modulus) {
161  x -= modulus;
162  }
163  return x;
164  }
165  if (InputModFactor == 8) {
166  FPGA_ASSERT(twice_modulus != nullptr,
167  "twice_modulus should not be nullptr");
168  FPGA_ASSERT(four_times_modulus != nullptr,
169  "four_times_modulus should not be nullptr");
170 
171  if (x >= *four_times_modulus) {
172  x -= *four_times_modulus;
173  }
174  if (x >= *twice_modulus) {
175  x -= *twice_modulus;
176  }
177  if (x >= modulus) {
178  x -= modulus;
179  }
180  return x;
181  }
182  FPGA_ASSERT(false, "Should be unreachable");
183  return x;
184 }
185 
186 inline uint64_t BarrettReduce128(uint64_t input_hi, uint64_t input_lo,
187  uint64_t modulus) {
188  FPGA_ASSERT(modulus != 0, "modulus == 0")
189  uint128_t n = (static_cast<uint128_t>(input_hi) << 64) |
190  (static_cast<uint128_t>(input_lo));
191 
192  return static_cast<uint64_t>(n % modulus);
193 }
194 
195 // Stores an integer on which modular multiplication can be performed more
196 // efficiently, at the cost of some precomputation.
198 public:
199  MultiplyFactor() = default;
200 
201  // Computes and stores the Barrett factor (operand << bit_shift) / modulus
202  MultiplyFactor(uint64_t operand, uint64_t bit_shift, uint64_t modulus)
203  : m_operand(operand) {
204  FPGA_ASSERT(operand <= modulus, "operand must be less than modulus");
205  FPGA_ASSERT(bit_shift == 64 || bit_shift == 52, "Unsupport BitShift");
206  uint64_t op_hi{0};
207  uint64_t op_lo{0};
208 
209  if (bit_shift == 64) {
210  op_hi = operand;
211  op_lo = 0;
212  } else if (bit_shift == 52) {
213  op_hi = operand >> 12;
214  op_lo = operand << 52;
215  }
216  m_barrett_factor = DivideUInt128UInt64Lo(op_hi, op_lo, modulus);
217  }
218 
219  inline uint64_t BarrettFactor() const { return m_barrett_factor; }
220  inline uint64_t Operand() const { return m_operand; }
221 
222 private:
223  uint64_t m_operand;
224  uint64_t m_barrett_factor;
225 };
226 
227 // Reverses the bits
228 uint64_t ReverseBitsUInt(uint64_t x, uint64_t bits);
229 
230 // Returns a^{-1} mod modulus
231 uint64_t InverseUIntMod(uint64_t a, uint64_t modulus);
232 
235 uint64_t MultiplyUIntMod(uint64_t x, uint64_t y, uint64_t modulus);
236 
237 // Returns (x * y) mod modulus
238 // @param y_precon floor(2**64 / modulus)
239 uint64_t MultiplyMod(uint64_t x, uint64_t y, uint64_t y_precon,
240  uint64_t modulus);
241 
242 // Returns (x + y) mod modulus
243 // Assumes x, y < modulus
244 uint64_t AddUIntMod(uint64_t x, uint64_t y, uint64_t modulus);
245 
246 // Returns (x - y) mod modulus
247 // Assumes x, y < modulus
248 uint64_t SubUIntMod(uint64_t x, uint64_t y, uint64_t modulus);
249 
250 // Returns base^exp mod modulus
251 uint64_t PowMod(uint64_t base, uint64_t exp, uint64_t modulus);
252 
253 // Returns true whether root is a degree-th root of unity
254 // degree must be a power of two.
255 bool IsPrimitiveRoot(uint64_t root, uint64_t degree, uint64_t modulus);
256 
257 // Tries to return a primtiive degree-th root of unity
258 // Returns -1 if no root is found
259 uint64_t GeneratePrimitiveRoot(uint64_t degree, uint64_t modulus);
260 
261 // Returns true whether root is a degree-th root of unity
262 // degree must be a power of two.
263 uint64_t MinimalPrimitiveRoot(uint64_t degree, uint64_t modulus);
264 
265 void ComputeRootOfUnityPowers(uint64_t m_q, uint64_t m_degree,
266  uint64_t m_degree_bits, uint64_t m_w,
267  uint64_t* inv_root_of_unity_powers,
268  uint64_t* precon64_inv_root_of_unity_powers,
269  uint64_t* root_of_unity_powers,
270  uint64_t* precon64_root_of_unity_powers);
271 } // namespace fpga
272 } // namespace hexl
273 } // namespace intel
274 
275 #endif
uint64_t MaximumValue(uint64_t bits)
Definition: number_theory_util.h:36
#define FPGA_ASSERT(...)
Definition: fpga_assert.h:15
uint64_t SubUIntMod(uint64_t x, uint64_t y, uint64_t modulus)
Definition: ntt.cpp:76
std::vector< uint64_t > GeneratePrimes(size_t num_primes, size_t bit_size, size_t ntt_size=1)
Definition: ntt.cpp:221
bool IsPrimitiveRoot(uint64_t root, uint64_t degree, uint64_t modulus)
Definition: ntt.cpp:99
unsigned char AddUInt64(uint64_t operand1, uint64_t operand2, uint64_t *result)
Definition: number_theory_util.h:115
bool IsPrime(uint64_t n)
Definition: ntt.cpp:173
uint64_t GeneratePrimitiveRoot(uint64_t degree, uint64_t modulus)
Definition: ntt.cpp:111
uint64_t AddUIntMod(uint64_t x, uint64_t y, uint64_t modulus)
Definition: ntt.cpp:69
__extension__ typedef unsigned __int128 uint128_t
Definition: number_theory_util.h:19
Definition: number_theory_util.h:197
uint128_t MultiplyUInt64(uint64_t x, uint64_t y)
Definition: number_theory_util.h:46
uint64_t BarrettReduce64(uint64_t input, uint64_t modulus, uint64_t q_barr)
Definition: ntt.cpp:45
MultiplyFactor(uint64_t operand, uint64_t bit_shift, uint64_t modulus)
Definition: number_theory_util.h:202
uint64_t ReverseBitsUInt(uint64_t x, uint64_t bits)
Definition: ntt.cpp:160
uint64_t DivideUInt128UInt64Lo(uint64_t x1, uint64_t x0, uint64_t y)
Definition: number_theory_util.h:68
uint64_t InverseUIntMod(uint64_t a, uint64_t modulus)
Definition: ntt.cpp:14
uint64_t ReduceMod(uint64_t x, uint64_t modulus, const uint64_t *twice_modulus=nullptr, const uint64_t *four_times_modulus=nullptr)
Definition: number_theory_util.h:139
uint64_t BarrettReduce128(uint64_t input_hi, uint64_t input_lo, uint64_t modulus)
Definition: number_theory_util.h:186
uint64_t MultiplyUInt64Hi(uint64_t x, uint64_t y)
Definition: number_theory_util.h:62
uint64_t MultiplyUIntMod(uint64_t x, uint64_t y, uint64_t modulus)
Definition: ntt.cpp:52
uint64_t MultiplyUIntModLazy(uint64_t x, uint64_t y_operand, uint64_t y_barrett_factor, uint64_t modulus)
Definition: number_theory_util.h:79
void ComputeRootOfUnityPowers(uint64_t m_q, uint64_t m_degree, uint64_t m_degree_bits, uint64_t m_w, uint64_t *inv_root_of_unity_powers, uint64_t *precon64_inv_root_of_unity_powers, uint64_t *root_of_unity_powers, uint64_t *precon64_root_of_unity_powers)
bool IsPowerOfTwo(uint64_t num)
Definition: number_theory_util.h:22
uint64_t Operand() const
Definition: number_theory_util.h:220
uint64_t MSB(uint64_t modulus)
Definition: number_theory_util.h:25
uint64_t PowMod(uint64_t base, uint64_t exp, uint64_t modulus)
Definition: ntt.cpp:84
uint64_t MinimalPrimitiveRoot(uint64_t degree, uint64_t modulus)
Definition: ntt.cpp:137
uint64_t BarrettFactor() const
Definition: number_theory_util.h:219
uint64_t MultiplyMod(uint64_t x, uint64_t y, uint64_t y_precon, uint64_t modulus)
Definition: ntt.cpp:62
uint64_t Log2(uint64_t x)
Definition: number_theory_util.h:30