├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── MODULE.bazel
├── MODULE.bazel.lock
├── README.md
└── shell_encryption
├── BUILD
├── aux_galois_key.h
├── aux_galois_key_test.cc
├── aux_relinearization_key.cc
├── aux_relinearization_key.h
├── aux_relinearization_key_test.cc
├── bits_util.h
├── bits_util_test.cc
├── constants.h
├── context.h
├── context_test.cc
├── dft_transformations.cc
├── dft_transformations.h
├── dft_transformations_hwy.cc
├── dft_transformations_hwy.h
├── dft_transformations_hwy_test.cc
├── dft_transformations_test.cc
├── error_params.h
├── error_params_test.cc
├── gadget.h
├── galois_key.h
├── galois_key_test.cc
├── int256.cc
├── int256.h
├── int256_test.cc
├── integral_types.h
├── modulus_conversion.h
├── montgomery.cc
├── montgomery.h
├── montgomery_test.cc
├── multi_party
├── BUILD
├── polynomial_utilities.h
├── public_key.cc
├── public_key.h
├── public_key_share.cc
├── public_key_share.h
├── public_key_share_test.cc
├── public_key_test.cc
├── public_parameter.cc
├── public_parameter.h
├── public_parameter_test.cc
├── recovery.h
├── recovery_test.cc
├── secret_key_share.cc
├── secret_key_share.h
├── secret_key_share_test.cc
├── serialization.proto
└── testing
│ ├── BUILD
│ └── parameters.h
├── ntt_parameters.cc
├── ntt_parameters.h
├── ntt_parameters_test.cc
├── oblivious_expand.h
├── oblivious_expand_test.cc
├── opt
├── BUILD
├── constant_polynomial.h
├── constant_polynomial_test.cc
├── lazy_polynomial.h
└── lazy_polynomial_test.cc
├── polynomial.h
├── polynomial_test.cc
├── prng
├── BUILD
├── LICENSE
├── chacha_prng.cc
├── chacha_prng.h
├── chacha_prng_test.cc
├── chacha_prng_util.cc
├── chacha_prng_util.h
├── hkdf_prng.cc
├── hkdf_prng.h
├── hkdf_prng_test.cc
├── hkdf_prng_util.cc
├── hkdf_prng_util.h
├── integral_prng_testing_types.h
├── prng.h
├── prng_test.cc
├── single_thread_chacha_prng.cc
├── single_thread_chacha_prng.h
├── single_thread_chacha_prng_test.cc
├── single_thread_hkdf_prng.cc
├── single_thread_hkdf_prng.h
└── single_thread_hkdf_prng_test.cc
├── public_key_encryption.cc
├── public_key_encryption.h
├── public_key_encryption_test.cc
├── relinearization_key.cc
├── relinearization_key.h
├── relinearization_key_test.cc
├── rns
├── BUILD
├── approximate_encoder.cc
├── approximate_encoder.h
├── approximate_encoder_test.cc
├── coefficient_encoder.cc
├── coefficient_encoder.h
├── coefficient_encoder_test.cc
├── constants.h
├── crt_interpolation.h
├── crt_interpolation_test.cc
├── error_correction.h
├── error_distribution.h
├── error_distribution_test.cc
├── finite_field_encoder.cc
├── finite_field_encoder.h
├── finite_field_encoder_test.cc
├── lazy_rns_polynomial.cc
├── lazy_rns_polynomial.h
├── lazy_rns_polynomial_test.cc
├── message_packing.h
├── message_packing_test.cc
├── rns_bfv_ciphertext.cc
├── rns_bfv_ciphertext.h
├── rns_bfv_ciphertext_test.cc
├── rns_bfv_public_key.cc
├── rns_bfv_public_key.h
├── rns_bfv_public_key_test.cc
├── rns_bgv_ciphertext.cc
├── rns_bgv_ciphertext.h
├── rns_bgv_ciphertext_test.cc
├── rns_bgv_public_key.cc
├── rns_bgv_public_key.h
├── rns_bgv_public_key_test.cc
├── rns_ciphertext.cc
├── rns_ciphertext.h
├── rns_ciphertext_test.cc
├── rns_context.cc
├── rns_context.h
├── rns_context_test.cc
├── rns_error_params.h
├── rns_error_params_test.cc
├── rns_gadget.cc
├── rns_gadget.h
├── rns_gadget_test.cc
├── rns_galois_key.cc
├── rns_galois_key.h
├── rns_galois_key_test.cc
├── rns_integer.h
├── rns_modulus.h
├── rns_polynomial.cc
├── rns_polynomial.h
├── rns_polynomial_hwy.cc
├── rns_polynomial_hwy.h
├── rns_polynomial_test.cc
├── rns_public_key.cc
├── rns_public_key.h
├── rns_relinearization_key.cc
├── rns_relinearization_key.h
├── rns_relinearization_key_test.cc
├── rns_secret_key.cc
├── rns_secret_key.h
├── rns_secret_key_test.cc
├── serialization.proto
└── testing
│ ├── BUILD
│ ├── parameters.h
│ └── testing_utils.h
├── sample_error.h
├── sample_error_test.cc
├── sampler
├── BUILD
├── discrete_gaussian.cc
├── discrete_gaussian.h
├── discrete_gaussian_test.cc
├── uniform_ternary.h
└── uniform_ternary_test.cc
├── serialization.proto
├── status_macros.h
├── status_macros_test.cc
├── statusor.h
├── symmetric_encryption.h
├── symmetric_encryption_test.cc
├── symmetric_encryption_with_prng.h
├── symmetric_encryption_with_prng_test.cc
├── testing
├── BUILD
├── LICENSE
├── coefficient_polynomial.h
├── coefficient_polynomial.proto
├── coefficient_polynomial_ciphertext.h
├── coefficient_polynomial_ciphertext_test.cc
├── coefficient_polynomial_test.cc
├── parameters.h
├── protobuf_matchers.h
├── protobuf_matchers_test.cc
├── status_matchers.h
├── status_testing.h
├── testing_prng.h
└── testing_utils.h
├── transcription.h
└── transcription_test.cc
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | bazel-*
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution,
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
--------------------------------------------------------------------------------
/MODULE.bazel:
--------------------------------------------------------------------------------
1 | module(
2 | name = "shell-encryption",
3 | )
4 |
5 | bazel_dep(name = "tink_cc", version = "2.4.0", repo_name = "com_github_tink_crypto_tink_cc")
6 | bazel_dep(name = "rules_cc", version = "0.2.9")
7 | bazel_dep(name = "rules_proto", version = "7.1.0")
8 | bazel_dep(name = "protobuf", version = "33.0-rc2", repo_name = "com_google_protobuf")
9 | bazel_dep(name = "googletest", version = "1.17.0.bcr.1", repo_name = "com_github_google_googletest")
10 | bazel_dep(name = "abseil-cpp", version = "20250814.1", repo_name = "com_google_absl")
11 | bazel_dep(name = "boringssl", version = "0.20251002.0")
12 | bazel_dep(name = "glog", version = "0.7.1", repo_name = "com_github_google_glog")
13 | bazel_dep(name = "rules_license", version = "1.0.0")
14 | bazel_dep(name = "highway", version = "1.3.0", repo_name = "com_github_google_highway")
15 |
--------------------------------------------------------------------------------
/shell_encryption/bits_util.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef RLWE_BITS_UTIL_H_
18 | #define RLWE_BITS_UTIL_H_
19 |
20 | #include
21 |
22 | #include "absl/numeric/int128.h"
23 | #include "shell_encryption/integral_types.h"
24 |
25 | namespace rlwe {
26 | namespace internal {
27 |
28 | inline unsigned int CountOnesInByte(Uint8 x) {
29 | Uint8 x0 = x & 0x55;
30 | Uint8 x1 = (x >> 1) & 0x55;
31 | x = x0 + x1;
32 |
33 | x0 = x & 0x33;
34 | x1 = (x >> 2) & 0x33;
35 | x = x0 + x1;
36 |
37 | x0 = x & 0x0F;
38 | x1 = (x >> 4) & 0x0F;
39 | return x0 + x1;
40 | }
41 |
42 | inline unsigned int CountOnes64(Uint64 x) {
43 | Uint64 x0 = x & 0x5555555555555555;
44 | Uint64 x1 = (x >> 1) & 0x5555555555555555;
45 | x = x0 + x1;
46 |
47 | x0 = x & 0x3333333333333333;
48 | x1 = (x >> 2) & 0x3333333333333333;
49 | x = x0 + x1;
50 |
51 | x0 = x & 0x0F0F0F0F0F0F0F0F;
52 | x1 = (x >> 4) & 0x0F0F0F0F0F0F0F0F;
53 | x = x0 + x1;
54 |
55 | x0 = x & 0x00FF00FF00FF00FF;
56 | x1 = (x >> 8) & 0x00FF00FF00FF00FF;
57 | x = x0 + x1;
58 |
59 | x0 = x & 0x0000FFFF0000FFFF;
60 | x1 = (x >> 16) & 0x0000FFFF0000FFFF;
61 | x = x0 + x1;
62 |
63 | x0 = x & 0x00000000FFFFFFFF;
64 | x1 = (x >> 32) & 0x00000000FFFFFFFF;
65 | return x0 + x1;
66 | }
67 |
68 | inline unsigned int CountLeadingZeros64(Uint64 x) {
69 | unsigned int zeros = 64;
70 | if (x >> 32) {
71 | zeros -= 32;
72 | x >>= 32;
73 | }
74 | if (x >> 16) {
75 | zeros -= 16;
76 | x >>= 16;
77 | }
78 | if (x >> 8) {
79 | zeros -= 8;
80 | x >>= 8;
81 | }
82 | if (x >> 4) {
83 | zeros -= 4;
84 | x >>= 4;
85 | }
86 | if (x >> 2) {
87 | zeros -= 2;
88 | x >>= 2;
89 | }
90 | if (x >> 1) {
91 | zeros -= 1;
92 | x >>= 1;
93 | }
94 | return zeros - x;
95 | }
96 |
97 | inline unsigned int CountLeadingZeros128(absl::uint128 x) {
98 | if (Uint64 hi = absl::Uint128High64(x)) return CountLeadingZeros64(hi);
99 | return CountLeadingZeros64(absl::Uint128Low64(x)) + 64;
100 | }
101 |
102 | inline unsigned int BitLength(absl::uint128 x) {
103 | return 128 - CountLeadingZeros128(x);
104 | }
105 |
106 | } // namespace internal
107 | } // namespace rlwe
108 |
109 | #endif // RLWE_BITS_UTIL_H_
110 |
--------------------------------------------------------------------------------
/shell_encryption/bits_util_test.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include "shell_encryption/bits_util.h"
16 |
17 | #include
18 | #include
19 | #include "absl/numeric/int128.h"
20 |
21 | using ::testing::Eq;
22 |
23 | namespace {
24 |
25 | TEST(BitsUtilTest, CountOnes64Works) {
26 | EXPECT_THAT(rlwe::internal::CountOnes64(0xFF000000000000FF), Eq(16));
27 | EXPECT_THAT(rlwe::internal::CountOnes64(0xFF000000000000FE), Eq(15));
28 | EXPECT_THAT(rlwe::internal::CountOnes64(0xFF0000000000FF00), Eq(16));
29 | EXPECT_THAT(rlwe::internal::CountOnes64(0x1111111111111111), Eq(16));
30 | EXPECT_THAT(rlwe::internal::CountOnes64(0x0321212121212121), Eq(16));
31 | }
32 |
33 | TEST(BitsUtilTest, CountOnesInByte) {
34 | EXPECT_THAT(rlwe::internal::CountOnesInByte(0x00), Eq(0));
35 | EXPECT_THAT(rlwe::internal::CountOnesInByte(0x01), Eq(1));
36 | EXPECT_THAT(rlwe::internal::CountOnesInByte(0x11), Eq(2));
37 | EXPECT_THAT(rlwe::internal::CountOnesInByte(0x22), Eq(2));
38 | EXPECT_THAT(rlwe::internal::CountOnesInByte(0x44), Eq(2));
39 | EXPECT_THAT(rlwe::internal::CountOnesInByte(0xFF), Eq(8));
40 | EXPECT_THAT(rlwe::internal::CountOnesInByte(0xEE), Eq(6));
41 | }
42 |
43 | TEST(BitsUtilTest, CountLeadingZeros64Works) {
44 | rlwe::Uint64 value = 0x8000000000000000;
45 | for (int i = 0; i < 64; i++) {
46 | EXPECT_THAT(rlwe::internal::CountLeadingZeros64(value), Eq(i));
47 | value >>= 1;
48 | }
49 | }
50 |
51 | TEST(BitsUtilTest, BitLengthWorks) {
52 | absl::uint128 value = absl::MakeUint128(0x8000000000000000, 0);
53 | for (int i = 0; i <= 128; i++) {
54 | EXPECT_THAT(rlwe::internal::BitLength(value), Eq(128 - i));
55 | value >>= 1;
56 | }
57 | }
58 |
59 | } // namespace
60 |
--------------------------------------------------------------------------------
/shell_encryption/constants.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #ifndef RLWE_CONSTANTS_H_
17 | #define RLWE_CONSTANTS_H_
18 |
19 | #include
20 |
21 | #include "absl/numeric/int128.h"
22 | #include "shell_encryption/integral_types.h"
23 |
24 | namespace rlwe {
25 |
26 | // To generate parameters for this Ring-LWE system, we need to choose a prime
27 | // modulus q. In order for us to perform NTT transforms on dimension 2^N
28 | // polynomials, we require 2^{N+1} to divide q-1. Thus, we set moduli to be
29 | // primes of the form q = p*2^{N+1} + 1, where N becomes our LogDegreeBound.
30 |
31 | // Parameters from the New Hope key exchange protocol. Note that we are not
32 | // using these parameters for key exchange in this library.
33 | constexpr Uint64 kNewhopeModulus = 12289;
34 | constexpr Uint64 kNewhopeLogDegreeBound = 10;
35 | constexpr Uint64 kNewhopeDegreeBound = 1 << kNewhopeLogDegreeBound;
36 |
37 | // RLWE parameters for a 62 bit modulus.
38 | constexpr Uint64 kModulus62 = 4611686018427322369;
39 | constexpr Uint64 kLogDegreeBound62 = 11;
40 | constexpr Uint64 kDegreeBound62 = 1L << kLogDegreeBound62;
41 |
42 | // Montgomery parameters for a 59-bit modulus.
43 | constexpr Uint64 kModulus59 = 332366567264636929;
44 | constexpr Uint64 kInvModulus59 = 7124357790306815999;
45 | constexpr Uint64 kLogDegreeBound59 = 10;
46 | constexpr Uint64 kDegreeBound59 = 1L << kLogDegreeBound59;
47 |
48 | // RLWE parameters for a 44-bit modulus.
49 | constexpr Uint64 kModulus44 = 17592169240577;
50 | constexpr Uint64 kLogDegreeBound44 = 10;
51 | constexpr Uint64 kDegreeBound44 = 1L << kLogDegreeBound44;
52 |
53 | // RLWE parameters for a 25-bit and a 29-bit moduli, both congruent to 4 modulo
54 | // 5 = (1 << 2) + 1. These moduli will be useful for testing of modulus
55 | // switching.
56 | constexpr Uint64 kModulus29 = 463187969;
57 | constexpr Uint64 kLogDegreeBound29 = 10;
58 | constexpr Uint64 kDegreeBound29 = 1L << kLogDegreeBound29;
59 | constexpr Uint64 kModulus25 = 33538049;
60 | constexpr Uint64 kLogDegreeBound25 = 10;
61 | constexpr Uint64 kDegreeBound25 = 1L << kLogDegreeBound25;
62 |
63 | // RLWE parameters for a 30 bit modulus.
64 | constexpr Uint64 kModulus30 = 1073707009;
65 | constexpr Uint64 kLogDegreeBound30 = 10;
66 | constexpr Uint64 kDegreeBound30 = 1L << kLogDegreeBound30;
67 |
68 | // RLWE parameters for an 80-bit modulus.
69 | // The modulus represented in decimal is 646119422561999443726337.
70 | constexpr absl::uint128 kModulus80 =
71 | absl::MakeUint128(35026, 3764636248688824321);
72 | constexpr Uint64 kLogDegreeBound80 = 11;
73 | constexpr Uint64 kDegreeBound80 = 1L << kLogDegreeBound80;
74 |
75 | constexpr Uint64 kMaxNumCoeffs = 1L << 15;
76 | constexpr Uint64 kMaxLogNumCoeffs = 15;
77 | constexpr Uint64 kMaxVariance = 256;
78 |
79 | } // namespace rlwe
80 |
81 | #endif // RLWE_CONSTANTS_H_
82 |
--------------------------------------------------------------------------------
/shell_encryption/context_test.cc:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #include "shell_encryption/context.h"
17 |
18 | #include
19 | #include
20 | #include "absl/numeric/int128.h"
21 | #include "shell_encryption/constants.h"
22 | #include "shell_encryption/integral_types.h"
23 | #include "shell_encryption/montgomery.h"
24 | #include "shell_encryption/status_macros.h"
25 | #include "shell_encryption/testing/parameters.h"
26 | #include "shell_encryption/testing/status_testing.h"
27 |
28 | namespace {
29 |
30 | template
31 | class ContextTest : public ::testing::Test {};
32 | TYPED_TEST_SUITE(ContextTest, rlwe::testing::ModularIntTypes);
33 |
34 | TYPED_TEST(ContextTest, CreateWorks) {
35 | for (const auto& params :
36 | rlwe::testing::ContextParameters::Value()) {
37 | ASSERT_OK_AND_ASSIGN(auto context,
38 | rlwe::RlweContext::Create(params));
39 | }
40 | }
41 |
42 | TYPED_TEST(ContextTest, ParametersMatch) {
43 | for (const auto& params :
44 | rlwe::testing::ContextParameters::Value()) {
45 | ASSERT_OK_AND_ASSIGN(auto context,
46 | rlwe::RlweContext::Create(params));
47 |
48 | ASSERT_EQ(context->GetLogN(), params.log_n);
49 | ASSERT_EQ(context->GetN(), context->GetNttParams()->number_coeffs);
50 | ASSERT_EQ(context->GetLogT(), params.log_t);
51 | ASSERT_EQ(context->GetModulus(), params.modulus);
52 | ASSERT_EQ(context->GetModulus(), context->GetModulusParams()->modulus);
53 | ASSERT_EQ(context->GetVariance(), params.variance);
54 | }
55 | }
56 |
57 | } // namespace
58 |
--------------------------------------------------------------------------------
/shell_encryption/dft_transformations.cc:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #include "shell_encryption/dft_transformations.h"
17 |
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | #include "absl/status/status.h"
24 |
25 | namespace rlwe {
26 |
27 | namespace {
28 |
29 | // Returns true if n is a power of two.
30 | inline bool IsPowerOfTwo(size_t n) { return (n & (n - 1)) == 0; }
31 |
32 | } // namespace
33 |
34 | absl::Status IterativeHalfCooleyTukey(
35 | std::vector>& coeffs,
36 | const std::vector>& psis_bitrev) {
37 | if (!IsPowerOfTwo(coeffs.size())) {
38 | return absl::InvalidArgumentError(
39 | "The size of `coeffs` must be a power of two.");
40 | }
41 | int len = coeffs.size();
42 | if (psis_bitrev.size() < len) {
43 | return absl::InvalidArgumentError(
44 | "Not enough primitive roots in `psis_bitrev`.");
45 | }
46 | int log_len = log2(len);
47 | for (int i = log_len - 1; i >= 0; i--) {
48 | // Layer i.
49 | const int half_m = 1 << i;
50 | const int m = half_m << 1;
51 | for (int k = 0, index_psi = 1 << (log_len - i); k < coeffs.size();
52 | k += m, ++index_psi) {
53 | const std::complex psi = psis_bitrev[index_psi];
54 | for (int j = 0; j < half_m; j++) {
55 | // The Cooley-Tukey butterfly operation.
56 | std::complex t = coeffs[k + j + half_m] * psi;
57 | std::complex u = coeffs[k + j];
58 | coeffs[k + j] += t;
59 | coeffs[k + j + half_m] = u - t;
60 | }
61 | }
62 | }
63 | return absl::OkStatus();
64 | }
65 |
66 | absl::Status IterativeHalfGentlemanSande(
67 | std::vector>& coeffs,
68 | const std::vector>& psis_bitrev_inv) {
69 | if (!IsPowerOfTwo(coeffs.size())) {
70 | return absl::InvalidArgumentError(
71 | "The size of `coeffs` must be a power of two.");
72 | }
73 | int len = coeffs.size();
74 | if (psis_bitrev_inv.size() < len * 2) {
75 | return absl::InvalidArgumentError(
76 | "Not enough primitive roots in `psis_bitrev_inv`.");
77 | }
78 | int log_len = log2(len);
79 | int index_psi_base = 0;
80 | for (int i = 0; i < log_len; i++) {
81 | const int half_m = 1 << i;
82 | const int m = half_m << 1;
83 | for (int k = 0, index_psi_inv = index_psi_base; k < coeffs.size();
84 | k += m, ++index_psi_inv) {
85 | for (int j = 0; j < half_m; j++) {
86 | // The Gentleman-Sande butterfly operation.
87 | std::complex t = coeffs[k + j + half_m];
88 | std::complex u = coeffs[k + j];
89 | coeffs[k + j] += t;
90 | coeffs[k + j + half_m] = (u - t) * psis_bitrev_inv[index_psi_inv];
91 | }
92 | }
93 | index_psi_base += 1 << (log_len - i);
94 | }
95 | return absl::OkStatus();
96 | }
97 |
98 | } // namespace rlwe
99 |
--------------------------------------------------------------------------------
/shell_encryption/dft_transformations_hwy.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #ifndef RLWE_DFT_TRANSFORMATIONS_HWY_H_
17 | #define RLWE_DFT_TRANSFORMATIONS_HWY_H_
18 |
19 | #include
20 | #include
21 |
22 | #include "absl/status/status.h"
23 | #include "shell_encryption/ntt_parameters.h"
24 | #include "shell_encryption/status_macros.h"
25 |
26 | namespace rlwe::internal {
27 |
28 | // Performs the forward NTT transformation from R_q = Z[X]/(q, X^N+1) to Z_q^N
29 | // in-place, where q is a prime and N is a power of two such q == 1 (mod 2N).
30 | // The input polynomial is represented by its coefficient vector `coeffs`.
31 | template
32 | extern absl::Status ForwardNumberTheoreticTransformHwy(
33 | std::vector& coeffs,
34 | const NttParameters& ntt_params,
35 | const typename ModularInt::Params& mod_params);
36 |
37 | } // namespace rlwe::internal
38 |
39 | #endif // RLWE_DFT_TRANSFORMATIONS_HWY_H_
40 |
--------------------------------------------------------------------------------
/shell_encryption/dft_transformations_hwy_test.cc:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #include "shell_encryption/dft_transformations_hwy.h"
17 |
18 | #include
19 | #include
20 |
21 | #include
22 | #include
23 | #include "absl/status/status.h"
24 | #include "absl/strings/str_cat.h"
25 | #include "hwy/targets.h"
26 | #include "shell_encryption/context.h"
27 | #include "shell_encryption/dft_transformations.h"
28 | #include "shell_encryption/status_macros.h"
29 | #include "shell_encryption/statusor.h"
30 | #include "shell_encryption/testing/parameters.h"
31 | #include "shell_encryption/testing/status_matchers.h"
32 | #include "shell_encryption/testing/status_testing.h"
33 | #include "shell_encryption/testing/testing_prng.h"
34 |
35 | #undef HWY_TARGET_INCLUDE
36 | #define HWY_TARGET_INCLUDE "shell_encryption/dft_transformations_hwy_test.cc"
37 | #include "hwy/foreach_target.h" // IWYU pragma: keep
38 | #include "hwy/highway.h"
39 |
40 | #if HWY_ONCE || HWY_IDE
41 |
42 | namespace rlwe::internal {
43 | namespace {
44 |
45 | using ::rlwe::testing::StatusIs;
46 | using ::testing::HasSubstr;
47 |
48 | template
49 | class DftTransformationsHwyTest : public ::testing::Test {
50 | using ModularIntParams = typename ModularInt::Params;
51 |
52 | protected:
53 | void SetUp() override {
54 | typename RlweContext::Parameters params =
55 | testing::ContextParameters::Value()[0];
56 | ASSERT_OK_AND_ASSIGN(context_, RlweContext::Create(params));
57 | prng_ = std::make_unique(0);
58 | }
59 |
60 | StatusOr> SampleCoeffs(
61 | int log_n, const ModularIntParams* mod_params) const {
62 | std::vector coeffs;
63 | for (int i = 0; i < (1 << log_n); ++i) {
64 | RLWE_ASSIGN_OR_RETURN(ModularInt coeff,
65 | ModularInt::ImportRandom(prng_.get(), mod_params));
66 | coeffs.push_back(std::move(coeff));
67 | }
68 | return coeffs;
69 | }
70 |
71 | std::unique_ptr> context_;
72 | std::unique_ptr prng_;
73 | };
74 |
75 | TYPED_TEST_SUITE(DftTransformationsHwyTest, testing::ModularIntTypes);
76 |
77 | TYPED_TEST(DftTransformationsHwyTest, ForwardNttFailsIfInvalidCoeffsLength) {
78 | // Create a vector of N+1 modular integers, where N is a power of two.
79 | int log_n = this->context_->GetLogN();
80 | std::vector coeffs(
81 | (1 << log_n) + 1,
82 | TypeParam::ImportZero(this->context_->GetModulusParams()));
83 |
84 | EXPECT_THAT(ForwardNumberTheoreticTransformHwy(
85 | coeffs, *this->context_->GetNttParams(),
86 | *this->context_->GetModulusParams()),
87 | StatusIs(absl::StatusCode::kInvalidArgument,
88 | HasSubstr(absl::StrCat("`coeffs` must have contain ",
89 | (1 << log_n), " elements"))));
90 | }
91 |
92 | TYPED_TEST(DftTransformationsHwyTest, ForwardNumberTheoreticTransform) {
93 | auto context_params = testing::ContextParameters::Value();
94 | // Add corner test cases where log degree is very small.
95 | context_params.push_back({/*.modulus =*/5, /*.log_n =*/1, /*.log_t =*/1,
96 | /*.variance =*/8});
97 | context_params.push_back({/*.modulus =*/17, /*.log_n =*/2, /*.log_t =*/1,
98 | /*.variance =*/8});
99 | for (const auto& params : context_params) {
100 | ASSERT_OK_AND_ASSIGN(auto context, RlweContext::Create(params));
101 | int log_n = context->GetLogN();
102 |
103 | ASSERT_OK_AND_ASSIGN(
104 | std::vector coeffs_original,
105 | this->SampleCoeffs(log_n, context->GetModulusParams()));
106 | std::vector coeffs_truth = coeffs_original;
107 | ASSERT_OK(ForwardNumberTheoreticTransform(
108 | coeffs_truth, *context->GetNttParams(), *context->GetModulusParams()));
109 | // It's not really desirable to test different targets in a single test, but
110 | // I can't seem to move these to parameterized tests.
111 | for (auto target : hwy::SupportedAndGeneratedTargets()) {
112 | hwy::SetSupportedTargetsForTest(target);
113 | std::vector coeffs = coeffs_original;
114 | ASSERT_OK(ForwardNumberTheoreticTransformHwy(
115 | coeffs, *context->GetNttParams(), *context->GetModulusParams()));
116 |
117 | EXPECT_EQ(coeffs.size(), (1 << log_n));
118 | for (int i = 0; i < coeffs.size(); ++i) {
119 | EXPECT_EQ(coeffs[i], coeffs_truth[i]);
120 | }
121 | hwy::SetSupportedTargetsForTest(0);
122 | }
123 | }
124 | }
125 |
126 | } // namespace
127 | } // namespace rlwe::internal
128 |
129 | #endif
130 |
--------------------------------------------------------------------------------
/shell_encryption/gadget.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #ifndef RLWE_GADGET_H_
17 | #define RLWE_GADGET_H_
18 |
19 | #include
20 | #include
21 |
22 | #include "shell_encryption/status_macros.h"
23 | #include "shell_encryption/statusor.h"
24 |
25 | // Common methods for working with "gadgets". These are ways of representing
26 | // large norm (but small dimension) objects with small norm (but large
27 | // dimension) objects. For example, one can take an integer |x| <= q, and write
28 | //
29 | // x = \sum_{i = 0}^{\log_2 q} x_i 2^i
30 | //
31 | // where the x_i are small. One can of course generalize this to digits B != 2.
32 | // The other main type of gadget is taking |x| <= \prod_i p_i for coprime values
33 | // p_i, and mapping
34 | //
35 | // x -> (x mod p_0, x mod p_1, ..., x mod p_k)
36 | //
37 | // For a formal introduction to gadgets, see https://eprint.iacr.org/2018/946.
38 |
39 | namespace rlwe {
40 |
41 | // Method to compute the number of digits needed to represent integers mod
42 | // q in base T.
43 | template
44 | inline int GadgetSize(int log_base,
45 | const typename ModularInt::Params* mod_params) {
46 | return (mod_params->log_modulus + (log_base - 1)) / log_base;
47 | }
48 |
49 | // Return the vector of base-B decomposition of each x mod q, where q is the
50 | // modulus defined in mod_params, ie return a vector of [v_0, ..., v_{k-1}]
51 | // such that sum(v_{j} * B^j) = x[i] mod q, and v_j \in [0, B)
52 | template
53 | StatusOr>> BaseDecompose(
54 | const std::vector& coeffs,
55 | const typename ModularInt::Params* mod_params, const size_t log_base,
56 | int dimension) {
57 | // Determine the dimension, which is ceil(log_base(q))
58 | std::vector curr_digits(coeffs.size(), 0);
59 | std::transform(
60 | coeffs.begin(), coeffs.end(), curr_digits.begin(),
61 | [mod_params](ModularInt x) { return x.ExportInt(mod_params); });
62 |
63 | // Compute the mask to extract the log_base least significant
64 | // bits
65 | typename ModularInt::Int mask =
66 | (static_cast(1) << log_base) - 1;
67 | std::vector> result(dimension);
68 | for (int i = 0; i < dimension; i++) {
69 | result[i].reserve(curr_digits.size());
70 | for (int j = 0; j < curr_digits.size(); ++j) {
71 | RLWE_ASSIGN_OR_RETURN(
72 | auto coefficient_part,
73 | ModularInt::ImportInt((curr_digits[j] & mask), mod_params));
74 | result[i].push_back(std::move(coefficient_part));
75 | curr_digits[j] >>= log_base;
76 | }
77 | }
78 | return result;
79 | }
80 |
81 | } // namespace rlwe
82 |
83 | #endif // RLWE_GADGET_H_
84 |
--------------------------------------------------------------------------------
/shell_encryption/integral_types.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef RLWE_INTEGRAL_TYPES_H_
18 | #define RLWE_INTEGRAL_TYPES_H_
19 |
20 | #include "absl/numeric/int128.h"
21 |
22 | namespace rlwe {
23 |
24 | typedef uint8_t Uint8;
25 | typedef uint16_t Uint16;
26 | typedef uint32_t Uint32;
27 | typedef uint64_t Uint64;
28 |
29 | #ifdef ABSL_HAVE_INTRINSIC_INT128
30 | typedef unsigned __int128 Uint128;
31 | #else
32 | typedef absl::uint128 Uint128;
33 | #endif
34 |
35 | } // namespace rlwe
36 |
37 | #endif // RLWE_INTEGRAL_TYPES_H_
38 |
--------------------------------------------------------------------------------
/shell_encryption/multi_party/public_key.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2025 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include "shell_encryption/multi_party/public_key.h"
16 |
17 | #include
18 |
19 | #include "absl/numeric/int128.h"
20 | #include "absl/status/status.h"
21 | #include "absl/status/statusor.h"
22 | #include "absl/types/span.h"
23 | #include "shell_encryption/integral_types.h"
24 | #include "shell_encryption/montgomery.h"
25 | #include "shell_encryption/multi_party/public_key_share.h"
26 | #include "shell_encryption/multi_party/public_parameter.h"
27 | #include "shell_encryption/rns/rns_polynomial.h"
28 | #include "shell_encryption/status_macros.h"
29 |
30 | namespace rlwe {
31 | namespace multi_party {
32 |
33 | template
34 | absl::StatusOr> PublicKey::Create(
35 | const PublicParameter* public_parameter,
36 | absl::Span> public_key_shares) {
37 | if (public_parameter == nullptr) {
38 | return absl::InvalidArgumentError("`public_parameter` must not be null.");
39 | }
40 | if (public_key_shares.empty()) {
41 | return absl::InvalidArgumentError("`public_key_shares` must not be empty.");
42 | }
43 |
44 | RLWE_ASSIGN_OR_RETURN(RnsPolynomial key_b,
45 | RnsPolynomial::CreateZero(
46 | public_parameter->LogN(),
47 | public_parameter->Moduli(), /*is_ntt=*/true));
48 | for (auto const& public_key_share : public_key_shares) {
49 | RLWE_RETURN_IF_ERROR(key_b.AddInPlace(public_key_share.ComponentB(),
50 | public_parameter->Moduli()));
51 | }
52 | return PublicKey(public_parameter, std::move(key_b));
53 | }
54 |
55 | template
56 | absl::StatusOr> PublicKey::Deserialize(
57 | const SerializedPublicKey& serialized,
58 | const PublicParameter* public_parameter) {
59 | if (public_parameter == nullptr) {
60 | return absl::InvalidArgumentError("`public_parameter` must not be null.");
61 | }
62 |
63 | RLWE_ASSIGN_OR_RETURN(RnsPolynomial key_b,
64 | RnsPolynomial::Deserialize(
65 | serialized.key_b(), public_parameter->Moduli()));
66 | return PublicKey(public_parameter, std::move(key_b));
67 | }
68 |
69 | template class PublicKey>;
70 | template class PublicKey>;
71 | template class PublicKey>;
72 | #ifdef ABSL_HAVE_INTRINSIC_INT128
73 | template class PublicKey>;
74 | #endif
75 |
76 | } // namespace multi_party
77 | } // namespace rlwe
78 |
--------------------------------------------------------------------------------
/shell_encryption/multi_party/public_key_share.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #ifndef RLWE_MULTI_PARTY_PUBLIC_KEY_SHARE_H_
17 | #define RLWE_MULTI_PARTY_PUBLIC_KEY_SHARE_H_
18 |
19 | #include
20 |
21 | #include "absl/status/status.h"
22 | #include "absl/status/statusor.h"
23 | #include "absl/types/span.h"
24 | #include "shell_encryption/multi_party/public_parameter.h"
25 | #include "shell_encryption/multi_party/secret_key_share.h"
26 | #include "shell_encryption/prng/prng.h"
27 | #include "shell_encryption/rns/rns_modulus.h"
28 | #include "shell_encryption/rns/rns_polynomial.h"
29 | #include "shell_encryption/status_macros.h"
30 |
31 | namespace rlwe {
32 | namespace multi_party {
33 |
34 | // Public key share of the RLWE-based multi-party homomorphic encryption scheme.
35 | // Every secret key share holder derives its own public key share, and by
36 | // combining all public key shares we get the public key of the multi-party
37 | // scheme.
38 | //
39 | // In the current protocol, a public key share is a polynomial b = -a * s + e,
40 | // where `a` is the random "a" component of the public key and is given in the
41 | // public parameter, and `e` is a fresh error polynomial.
42 | template
43 | class PublicKeyShare {
44 | public:
45 | // Returns a public key share based on the given `secret_key_share`.
46 | static absl::StatusOr Create(
47 | const SecretKeyShare* secret_key_share,
48 | const PublicParameter* public_parameter, PrngType prng_type);
49 |
50 | // Creates a new public key share derived from `secret_key_share` and the "a"
51 | // component of the public key as given in `public_parameter`, and populates
52 | // `key_b` with the raw key share polynomial in NTT form. Randomness is
53 | // sampled using `prng`.
54 | // `key_error` is optional and can be null. When it is non-null, `key_error`
55 | // is populated with the error polynomial in NTT form such that
56 | // key_b = -a(X) * secret_key_share + key_error \in Z[X]/(Q, X^N+1),
57 | // where a(X) is the "a" component of the public key.
58 | // `wrap_around` is optional and can be null. When it is non-null,
59 | // `wrap_around` is populated in coefficient form such that
60 | // key_b = -a(X) * secret_key_share + key_error + wrap_around * (X^N + 1)
61 | // over the ring Z[X]/(Q), i.e. the RHS is computed without reduction modulo
62 | // X^N + 1, and the LHS is defined in the first equation above.
63 | //
64 | // The optional `key_error` and `wrap_around` are useful to generate ZK proofs
65 | // about valid public key shares.
66 | //
67 | // Note: When `wrap_around` is non-null, the parameters must be NTT-friendly
68 | // wrt to the larger cyclotomic X^{2N} + 1, i.e. N is a power of 2 and
69 | // the modulus Q must be a product of primes q such that 4N factors q-1.
70 | static absl::Status CreateExplicit(
71 | const RnsPolynomial& secret_key_share,
72 | const PublicParameter* public_parameter, SecurePrng* prng,
73 | RnsPolynomial* key_b, RnsPolynomial* key_error,
74 | RnsPolynomial* wrap_around);
75 |
76 | static absl::StatusOr Deserialize(
77 | const SerializedPublicKeyShare& serialized,
78 | absl::Span* const> moduli);
79 |
80 | absl::StatusOr Serialize() const {
81 | SerializedPublicKeyShare serialized;
82 | RLWE_ASSIGN_OR_RETURN(*serialized.mutable_key_b(),
83 | key_b_.Serialize(moduli_));
84 | return serialized;
85 | }
86 |
87 | // Accessor to the "b" component in a public key share.
88 | const RnsPolynomial& ComponentB() const { return key_b_; }
89 |
90 | private:
91 | explicit PublicKeyShare(RnsPolynomial key_b,
92 | std::vector*> moduli)
93 | : key_b_(std::move(key_b)), moduli_(std::move(moduli)) {}
94 |
95 | // The "b" component of the public key share.
96 | RnsPolynomial key_b_;
97 |
98 | // The prime moduli constituting the modulus of this ciphertext.
99 | std::vector*> moduli_;
100 | };
101 |
102 | } // namespace multi_party
103 | } // namespace rlwe
104 |
105 | #endif // RLWE_MULTI_PARTY_PUBLIC_KEY_SHARE_H_
106 |
--------------------------------------------------------------------------------
/shell_encryption/multi_party/public_parameter.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #ifndef RLWE_MULTI_PARTY_PUBLIC_PARAMETER_H_
17 | #define RLWE_MULTI_PARTY_PUBLIC_PARAMETER_H_
18 |
19 | #include
20 | #include
21 | #include
22 |
23 | #include "absl/status/status.h"
24 | #include "absl/status/statusor.h"
25 | #include "absl/types/span.h"
26 | #include "shell_encryption/multi_party/serialization.pb.h"
27 | #include "shell_encryption/prng/prng.h"
28 | #include "shell_encryption/rns/rns_context.h"
29 | #include "shell_encryption/rns/rns_modulus.h"
30 | #include "shell_encryption/rns/rns_polynomial.h"
31 | #include "shell_encryption/status_macros.h"
32 |
33 | namespace rlwe {
34 | namespace multi_party {
35 |
36 | // This class stores the public parameter of multi-party additive homomorphic
37 | // encryption protocol, which is used by all parties to generate their public
38 | // key shares.
39 | template
40 | class PublicParameter {
41 | public:
42 | // Deterministic factory function to create a public parameter from a seed.
43 | static absl::StatusOr> CreateFromSeed(
44 | const RnsContext* rns_context, int error_variance,
45 | std::string prng_seed, PrngType prng_type);
46 |
47 | // Factory function to create a fresh public parameter for the parties to
48 | // generate their public key shares.
49 | static absl::StatusOr> Create(
50 | const RnsContext* rns_context, int error_variance,
51 | PrngType prng_type);
52 |
53 | static absl::StatusOr> Deserialize(
54 | const SerializedPublicParameter& serialized,
55 | const RnsContext* rns_context);
56 |
57 | absl::StatusOr Serialize() const {
58 | SerializedPublicParameter serialized;
59 | serialized.set_prng_seed(prng_seed_);
60 | serialized.set_prng_type(prng_type_);
61 | serialized.set_error_variance(error_variance_);
62 | return serialized;
63 | }
64 |
65 | // Accessors.
66 | int ErrorVariance() const { return error_variance_; }
67 |
68 | const RnsPolynomial& PublicKeyComponentA() const {
69 | return key_a_;
70 | }
71 |
72 | absl::Span* const> Moduli() const {
73 | return moduli_;
74 | }
75 |
76 | int LogN() const { return key_a_.LogN(); }
77 | int NumCoeffs() const { return key_a_.NumCoeffs(); }
78 | int NumModuli() const { return moduli_.size(); }
79 |
80 | private:
81 | explicit PublicParameter(std::string prng_seed, PrngType prng_type,
82 | int error_variance, RnsPolynomial key_a,
83 | std::vector*> moduli)
84 | : prng_seed_(std::move(prng_seed)),
85 | prng_type_(prng_type),
86 | error_variance_(error_variance),
87 | key_a_(std::move(key_a)),
88 | moduli_(std::move(moduli)) {}
89 |
90 | // PRNG seed and type for sampling the random polynomial `key_a_`.
91 | const std::string prng_seed_;
92 | const PrngType prng_type_;
93 |
94 | // The variance for generating the public key and for encrypting using the
95 | // public key.
96 | const int error_variance_;
97 |
98 | // The "a" component of the public key.
99 | const RnsPolynomial key_a_;
100 |
101 | // The RNS moduli used by the public key.
102 | const std::vector*> moduli_;
103 | };
104 |
105 | } // namespace multi_party
106 | } // namespace rlwe
107 |
108 | #endif // RLWE_MULTI_PARTY_PUBLIC_PARAMETER_H_
109 |
--------------------------------------------------------------------------------
/shell_encryption/multi_party/recovery.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #ifndef RLWE_MULTI_PARTY_RECOVERY_H_
17 | #define RLWE_MULTI_PARTY_RECOVERY_H_
18 |
19 | #include
20 |
21 | #include "absl/status/status.h"
22 | #include "absl/status/statusor.h"
23 | #include "absl/types/span.h"
24 | #include "shell_encryption/multi_party/public_parameter.h"
25 | #include "shell_encryption/rns/coefficient_encoder.h"
26 | #include "shell_encryption/rns/rns_polynomial.h"
27 | #include "shell_encryption/status_macros.h"
28 |
29 | namespace rlwe {
30 | namespace multi_party {
31 |
32 | template >
34 | absl::StatusOr> RecoverMessages(
35 | absl::Span> partial_decryptions,
36 | const RnsPolynomial& ciphertext_component_b,
37 | const PublicParameter& public_parameter,
38 | const Encoder* encoder) {
39 | if (partial_decryptions.empty()) {
40 | return absl::InvalidArgumentError(
41 | "`partial_decryptions` must not be empty.");
42 | }
43 |
44 | auto moduli = public_parameter.Moduli();
45 | RLWE_ASSIGN_OR_RETURN(RnsPolynomial noisy_plaintext,
46 | RnsPolynomial::CreateZero(
47 | public_parameter.LogN(), moduli, /*is_ntt=*/true));
48 |
49 | for (auto const& partial_decryption : partial_decryptions) {
50 | RLWE_RETURN_IF_ERROR(
51 | noisy_plaintext.AddInPlace(partial_decryption, moduli));
52 | }
53 |
54 | return RecoverMessagesFromSum(noisy_plaintext, ciphertext_component_b,
55 | public_parameter, encoder);
56 | }
57 |
58 | template >
60 | absl::StatusOr> RecoverMessagesFromSum(
61 | const RnsPolynomial& sum_partial_decryptions,
62 | const RnsPolynomial& ciphertext_component_b,
63 | const PublicParameter& public_parameter,
64 | const Encoder* encoder) {
65 | if (encoder == nullptr) {
66 | return absl::InvalidArgumentError("`encoder` must not be null.");
67 | }
68 |
69 | auto moduli = public_parameter.Moduli();
70 |
71 | RLWE_ASSIGN_OR_RETURN(
72 | RnsPolynomial noisy_plaintext,
73 | sum_partial_decryptions.Add(ciphertext_component_b, moduli));
74 |
75 | // Decode the noisy plaintext polynomial.
76 | return encoder->DecodeBfv(std::move(noisy_plaintext), moduli);
77 | }
78 |
79 | } // namespace multi_party
80 | } // namespace rlwe
81 |
82 | #endif // RLWE_MULTI_PARTY_RECOVERY_H_
83 |
--------------------------------------------------------------------------------
/shell_encryption/multi_party/secret_key_share.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2025 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include "shell_encryption/multi_party/secret_key_share.h"
16 |
17 | #include
18 | #include
19 |
20 | #include "absl/numeric/int128.h"
21 | #include "absl/status/status.h"
22 | #include "absl/status/statusor.h"
23 | #include "absl/types/span.h"
24 | #include "shell_encryption/integral_types.h"
25 | #include "shell_encryption/montgomery.h"
26 | #include "shell_encryption/multi_party/polynomial_utilities.h"
27 | #include "shell_encryption/prng/prng.h"
28 | #include "shell_encryption/rns/error_distribution.h"
29 | #include "shell_encryption/rns/rns_context.h"
30 | #include "shell_encryption/rns/rns_modulus.h"
31 | #include "shell_encryption/rns/rns_polynomial.h"
32 | #include "shell_encryption/sampler/discrete_gaussian.h"
33 | #include "shell_encryption/status_macros.h"
34 |
35 | namespace rlwe {
36 | namespace multi_party {
37 |
38 | template
39 | absl::StatusOr> SecretKeyShare::Sample(
40 | const RnsContext* rns_context, SecurePrng* prng) {
41 | if (rns_context == nullptr) {
42 | return absl::InvalidArgumentError("`rns_context` must not be null.");
43 | }
44 | if (prng == nullptr) {
45 | return absl::InvalidArgumentError("`prng` must not be null.");
46 | }
47 |
48 | // Sample the secret s with uniform ternary coefficients.
49 | int log_n = rns_context->LogN();
50 | std::vector*> moduli =
51 | rns_context->MainPrimeModuli();
52 | RLWE_ASSIGN_OR_RETURN(RnsPolynomial s,
53 | SampleUniformTernary(log_n, moduli, prng));
54 | return SecretKeyShare(std::move(s), std::move(moduli));
55 | }
56 |
57 | template
58 | absl::StatusOr>
59 | SecretKeyShare::PartialDecrypt(
60 | const RnsPolynomial& ciphertext_component_a, double s_flood,
61 | const DiscreteGaussianSampler* dg_sampler, SecurePrng* prng,
62 | RnsPolynomial* error_flood,
63 | RnsPolynomial* wrap_around) const {
64 | if (dg_sampler == nullptr) {
65 | return absl::InvalidArgumentError("`dg_sampler` must not be null.");
66 | }
67 | if (prng == nullptr) {
68 | return absl::InvalidArgumentError("`prng` must not be null.");
69 | }
70 |
71 | // Sample e_flood.
72 | RLWE_ASSIGN_OR_RETURN(RnsPolynomial d,
73 | SampleDiscreteGaussian(
74 | LogN(), s_flood, moduli_, dg_sampler, prng));
75 | if (error_flood != nullptr) {
76 | *error_flood = d;
77 | }
78 |
79 | if (!d.IsNttForm()) {
80 | RLWE_RETURN_IF_ERROR(d.ConvertToNttForm(moduli_));
81 | }
82 |
83 | RnsPolynomial c1 = ciphertext_component_a;
84 | if (!c1.IsNttForm()) {
85 | RLWE_RETURN_IF_ERROR(c1.ConvertToNttForm(moduli_));
86 | }
87 |
88 | // c1s = c1 * s.
89 | RLWE_ASSIGN_OR_RETURN(RnsPolynomial c1s, c1.Mul(key_, moduli_));
90 |
91 | // d = c1 * s + e_flood.
92 | RLWE_RETURN_IF_ERROR(d.AddInPlace(c1s, moduli_));
93 |
94 | // Optionally save wraparound such that
95 | // d = c1 * s + e_flood + wraparound * (X^N + 1) over Z_Q[X].
96 | if (wrap_around != nullptr) {
97 | RLWE_ASSIGN_OR_RETURN(
98 | *wrap_around,
99 | rlwe_internal::QuotientOf(c1, key_, c1s, absl::MakeSpan(moduli_)));
100 | }
101 |
102 | return d;
103 | }
104 |
105 | template class SecretKeyShare>;
106 | template class SecretKeyShare>;
107 | template class SecretKeyShare>;
108 | #ifdef ABSL_HAVE_INTRINSIC_INT128
109 | template class SecretKeyShare>;
110 | #endif
111 |
112 | } // namespace multi_party
113 | } // namespace rlwe
114 |
--------------------------------------------------------------------------------
/shell_encryption/multi_party/secret_key_share.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #ifndef RLWE_MULTI_PARTY_SECRET_KEY_SHARE_H_
17 | #define RLWE_MULTI_PARTY_SECRET_KEY_SHARE_H_
18 |
19 | #include
20 |
21 | #include "absl/status/status.h"
22 | #include "absl/status/statusor.h"
23 | #include "absl/types/span.h"
24 | #include "shell_encryption/prng/prng.h"
25 | #include "shell_encryption/rns/rns_context.h"
26 | #include "shell_encryption/rns/rns_modulus.h"
27 | #include "shell_encryption/rns/rns_polynomial.h"
28 | #include "shell_encryption/sampler/discrete_gaussian.h"
29 |
30 | namespace rlwe {
31 | namespace multi_party {
32 |
33 | // Secret key share of the RLWE-based multi-party homomorphic encryption scheme.
34 | // Every secret key share is held by a party in the protocol, and it is used to
35 | // partially decrypt a ciphertext encrypted under the multi-party public key.
36 | template
37 | class SecretKeyShare {
38 | public:
39 | using Integer = typename ModularInt::Int;
40 |
41 | // Samples a secret key share from the uniform ternary distribution, wrt the
42 | // RNS `moduli`.
43 | static absl::StatusOr Sample(
44 | const RnsContext* rns_context, SecurePrng* prng);
45 |
46 | // Returns the partial decryption contribution from this secret key share
47 | // holder, for a ciphertext whose "a" component is given.
48 | absl::StatusOr> PartialDecrypt(
49 | const RnsPolynomial& ciphertext_component_a, double s_flood,
50 | const DiscreteGaussianSampler* dg_sampler, SecurePrng* prng,
51 | RnsPolynomial* error_flood,
52 | RnsPolynomial* wrap_around) const;
53 |
54 | // Accessors
55 | int LogN() const { return key_.LogN(); }
56 | int NumCoeffs() const { return key_.NumCoeffs(); }
57 | int NumModuli() const { return moduli_.size(); }
58 |
59 | // Accessor for the prime moduli chain.
60 | absl::Span* const> Moduli() const {
61 | return moduli_;
62 | }
63 |
64 | // Accessor for the key polynomial
65 | const RnsPolynomial& Key() const { return key_; }
66 |
67 | // For Rust interoperability. Defined in the wrapper library, not here.
68 | friend class SecretKeyShareRawFactory;
69 |
70 | private:
71 | explicit SecretKeyShare(RnsPolynomial key,
72 | std::vector*> moduli)
73 | : key_(std::move(key)), moduli_(std::move(moduli)) {}
74 |
75 | // The key polynomial
76 | RnsPolynomial key_;
77 |
78 | // The prime moduli constituting the modulus of this ciphertext.
79 | std::vector*> moduli_;
80 | };
81 |
82 | } // namespace multi_party
83 | } // namespace rlwe
84 |
85 | #endif // RLWE_MULTI_PARTY_SECRET_KEY_SHARE_H_
86 |
--------------------------------------------------------------------------------
/shell_encryption/multi_party/serialization.proto:
--------------------------------------------------------------------------------
1 | // Copyright 2025 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | syntax = "proto2";
16 |
17 | package rlwe;
18 |
19 | import "shell_encryption/serialization.proto";
20 | import "shell_encryption/rns/serialization.proto";
21 |
22 | option optimize_for = LITE_RUNTIME;
23 |
24 | // Public parameter.
25 | message SerializedPublicParameter {
26 | // Seed used to generate the random polynomial "a" in the public parameter.
27 | optional bytes prng_seed = 1;
28 |
29 | // Type of PRNG used to generate the random polynomial "a"
30 | optional PrngType prng_type = 2;
31 |
32 | // The variance of error distribution for generating the public key.
33 | optional int32 error_variance = 3;
34 | }
35 |
36 | // Public key share.
37 | message SerializedPublicKeyShare {
38 | // The "b" components of the public key share.
39 | optional SerializedRnsPolynomial key_b = 1;
40 | }
41 |
42 | // Public key.
43 | message SerializedPublicKey {
44 | // The "b" components of the public key.
45 | optional SerializedRnsPolynomial key_b = 1;
46 | }
47 |
--------------------------------------------------------------------------------
/shell_encryption/multi_party/testing/BUILD:
--------------------------------------------------------------------------------
1 | # Copyright 2025 Google LLC.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | load("@rules_cc//cc:cc_library.bzl", "cc_library")
16 |
17 | package(default_visibility = ["//visibility:public"])
18 |
19 | cc_library(
20 | name = "parameters",
21 | testonly = 1,
22 | hdrs = ["parameters.h"],
23 | deps = [
24 | "//shell_encryption:integral_types",
25 | "//shell_encryption:montgomery",
26 | "@com_github_google_googletest//:gtest",
27 | ],
28 | )
29 |
--------------------------------------------------------------------------------
/shell_encryption/multi_party/testing/parameters.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #ifndef RLWE_MULTI_PARTY_TESTING_PARAMETERS_H_
17 | #define RLWE_MULTI_PARTY_TESTING_PARAMETERS_H_
18 |
19 | #include
20 |
21 | #include
22 | #include "shell_encryption/integral_types.h"
23 | #include "shell_encryption/montgomery.h"
24 |
25 | namespace rlwe {
26 | namespace multi_party {
27 | namespace testing {
28 |
29 | using ModularInt32 = MontgomeryInt;
30 | using ModularInt64 = MontgomeryInt;
31 | using ModularIntTypesForMultiParty =
32 | ::testing::Types, rlwe::MontgomeryInt>;
33 | using ModularIntTypesForNegativeTests = ::testing::Types;
34 |
35 | // Aggregate type that holds the parameters defining multi-party BFV protocol.
36 | template
37 | struct MpaheParameters {
38 | int log_n;
39 | std::vector qs; // main prime moduli.
40 | std::vector ps; // auxiliary prime moduli.
41 | typename ModularInt::Int t; // plaintext modulus.
42 | int log_gadget_base; // Bit size of gadget base.
43 | double s_flood; // Gaussian parameter for flooding noise.
44 | };
45 |
46 | // Returns the testing parameters for the underlying integer type.
47 | template
48 | MpaheParameters GetMultiPartyParameters();
49 |
50 | // Returns the testing parameters when instantiating RNS using 32-bit integers.
51 | // The prime moduli in `qs` must be NTT-friendly for the higher order cyclotomic
52 | // X^{2N} + 1 to allow efficient computation of the "wrap around" polynomial for
53 | // the public key share.
54 | template <>
55 | inline MpaheParameters GetMultiPartyParameters() {
56 | return
57 | // 60 bits main modulus.
58 | MpaheParameters{.log_n = 11,
59 | .qs = {1073692673, 1073643521},
60 | .ps = {},
61 | .t = 10001,
62 | .log_gadget_base = 5,
63 | .s_flood = 1.0e+10};
64 | }
65 |
66 | // Returns the testing parameters when instantiating RNS using 64-bit integers.
67 | // Same as above, the prime moduli in `qs` must be NTT friendly for X^{2N} + 1.
68 | template <>
69 | inline MpaheParameters GetMultiPartyParameters() {
70 | return
71 | // 69 bits main modulus.
72 | MpaheParameters{.log_n = 12,
73 | .qs = {34359410689ULL, 34359214081ULL},
74 | .ps = {},
75 | .t = 54001,
76 | .log_gadget_base = 5,
77 | .s_flood = 4.25839e+13};
78 | }
79 |
80 | } // namespace testing
81 | } // namespace multi_party
82 | } // namespace rlwe
83 |
84 | #endif // RLWE_MULTI_PARTY_TESTING_PARAMETERS_H_
85 |
--------------------------------------------------------------------------------
/shell_encryption/ntt_parameters.cc:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 | #include "shell_encryption/ntt_parameters.h"
16 |
17 | namespace rlwe {
18 | namespace internal {
19 |
20 | // Bit reverse only among the rightmost log_n bytes.
21 | unsigned int Bitrev(unsigned int input, unsigned int log_n) {
22 | unsigned int output = 0;
23 | for (unsigned int i = 0; i < log_n; i++) {
24 | output <<= 1;
25 | output |= input & 0x01;
26 | input >>= 1;
27 | }
28 |
29 | return output;
30 | }
31 |
32 | std::vector BitrevArray(unsigned int log_n) {
33 | unsigned int n = 1 << log_n;
34 | std::vector output(n);
35 |
36 | for (unsigned int i = 0; i < n; i++) {
37 | output[i] = Bitrev(i, log_n);
38 | }
39 |
40 | return output;
41 | }
42 |
43 | } // namespace internal
44 | } // namespace rlwe
45 |
--------------------------------------------------------------------------------
/shell_encryption/opt/BUILD:
--------------------------------------------------------------------------------
1 | # Copyright 2021 Google LLC.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | load("@rules_cc//cc:cc_library.bzl", "cc_library")
16 | load("@rules_cc//cc:cc_test.bzl", "cc_test")
17 |
18 | package(default_visibility = ["//visibility:public"])
19 |
20 | licenses(["notice"])
21 |
22 | cc_library(
23 | name = "constant_polynomial",
24 | hdrs = ["constant_polynomial.h"],
25 | deps = [
26 | "//shell_encryption:statusor_fork",
27 | "@com_google_absl//absl/status",
28 | ],
29 | )
30 |
31 | cc_test(
32 | name = "constant_polynomial_test",
33 | srcs = ["constant_polynomial_test.cc"],
34 | deps = [
35 | ":constant_polynomial",
36 | "//shell_encryption:context",
37 | "//shell_encryption:polynomial",
38 | "//shell_encryption/testing:parameters",
39 | "//shell_encryption/testing:status_is_fork",
40 | "//shell_encryption/testing:status_testing",
41 | "@com_github_google_googletest//:gtest_main",
42 | ],
43 | )
44 |
45 | cc_library(
46 | name = "lazy_polynomial",
47 | hdrs = ["lazy_polynomial.h"],
48 | deps = [
49 | "//shell_encryption:statusor_fork",
50 | "@com_google_absl//absl/status",
51 | "@com_google_absl//absl/strings",
52 | ],
53 | )
54 |
55 | cc_test(
56 | name = "lazy_polynomial_test",
57 | srcs = ["lazy_polynomial_test.cc"],
58 | deps = [
59 | ":lazy_polynomial",
60 | "//shell_encryption:context",
61 | "//shell_encryption:montgomery",
62 | "//shell_encryption:polynomial",
63 | "//shell_encryption:statusor_fork",
64 | "//shell_encryption/prng:single_thread_hkdf_prng",
65 | "//shell_encryption/testing:parameters",
66 | "//shell_encryption/testing:status_is_fork",
67 | "//shell_encryption/testing:status_testing",
68 | "@com_github_google_googletest//:gtest_main",
69 | "@com_google_absl//absl/status",
70 | ],
71 | )
72 |
--------------------------------------------------------------------------------
/shell_encryption/opt/constant_polynomial.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #ifndef RLWE_OPT_CONSTANT_POLYNOMIAL_H_
17 | #define RLWE_OPT_CONSTANT_POLYNOMIAL_H_
18 |
19 | #include
20 |
21 | #include "absl/status/status.h"
22 | #include "shell_encryption/statusor.h"
23 |
24 | namespace rlwe {
25 |
26 | // Forward declaration of a Polynomial.
27 | template
28 | class Polynomial;
29 |
30 | // This class defines a constant polynomial, which cannot be operated on, but
31 | // enable to speed up polynomial multiplications.
32 | template
33 | class ConstantPolynomial {
34 | public:
35 | using Int = typename ModularInt::Int;
36 |
37 | // Delete default constructor.
38 | ConstantPolynomial() = delete;
39 |
40 | // Factory function to create a ConstantPolynomial.
41 | static StatusOr Create(
42 | std::vector constant, std::vector constant_barrett) {
43 | if (constant.size() != constant_barrett.size()) {
44 | return absl::InvalidArgumentError(
45 | "The vectors of Int do not have the same size.");
46 | }
47 | return ConstantPolynomial(std::move(constant), std::move(constant_barrett));
48 | }
49 |
50 | // Get the length.
51 | size_t Len() const { return coeffs_constant_.size(); }
52 |
53 | private:
54 | // Private constructor.
55 | ConstantPolynomial(std::vector constant,
56 | std::vector constant_barrett)
57 | : coeffs_constant_(std::move(constant)),
58 | coeffs_constant_barrett_(std::move(constant_barrett)) {}
59 |
60 | // Enable MulConstantInPlace() and FusedMulConstantAddInPlace() inside
61 | // Polynomial to access internal members.
62 | friend absl::Status Polynomial::MulConstantInPlace(
63 | const ConstantPolynomial& that,
64 | const typename ModularInt::Params* modular_params);
65 | friend absl::Status Polynomial::FusedMulConstantAddInPlace(
66 | const Polynomial& a, const ConstantPolynomial& b,
67 | const typename ModularInt::Params* modular_params);
68 |
69 | // Constant private members only usable from the Polynomial class.
70 | const std::vector coeffs_constant_;
71 | const std::vector coeffs_constant_barrett_;
72 | };
73 |
74 | } // namespace rlwe
75 |
76 | #endif // RLWE_OPT_CONSTANT_POLYNOMIAL_H_
77 |
--------------------------------------------------------------------------------
/shell_encryption/opt/constant_polynomial_test.cc:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #include "shell_encryption/opt/constant_polynomial.h"
17 |
18 | #include
19 | #include
20 | #include "shell_encryption/context.h"
21 | #include "shell_encryption/polynomial.h" // Needs to be included since Polynomial is forward declarated.
22 | #include "shell_encryption/testing/parameters.h"
23 | #include "shell_encryption/testing/status_matchers.h"
24 | #include "shell_encryption/testing/status_testing.h"
25 |
26 | namespace {
27 |
28 | using rlwe::testing::StatusIs;
29 |
30 | template
31 | class ConstantPolynomialTest : public ::testing::Test {};
32 | TYPED_TEST_SUITE(ConstantPolynomialTest, rlwe::testing::ModularIntTypes);
33 |
34 | TYPED_TEST(ConstantPolynomialTest, Works) {
35 | for (const auto& params :
36 | rlwe::testing::ContextParameters::Value()) {
37 | ASSERT_OK_AND_ASSIGN(auto context,
38 | rlwe::RlweContext::Create(params));
39 | std::vector constant, constant_barrett;
40 |
41 | for (auto length_constant : {1, 10, 1024}) {
42 | constant.resize(length_constant);
43 | for (auto length_constant_barrett : {1, 10, 1024}) {
44 | constant_barrett.resize(length_constant_barrett);
45 |
46 | if (length_constant == length_constant_barrett) {
47 | ASSERT_OK_AND_ASSIGN(auto p,
48 | rlwe::ConstantPolynomial::Create(
49 | constant, constant_barrett));
50 | } else {
51 | EXPECT_THAT(
52 | rlwe::ConstantPolynomial::Create(constant,
53 | constant_barrett),
54 | StatusIs(::absl::StatusCode::kInvalidArgument,
55 | "The vectors of Int do not have the same size."));
56 | }
57 | }
58 | }
59 | }
60 | }
61 |
62 | } // namespace
63 |
--------------------------------------------------------------------------------
/shell_encryption/prng/chacha_prng.cc:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #include "shell_encryption/prng/chacha_prng.h"
17 |
18 | #include
19 |
20 | #include "absl/memory/memory.h"
21 | #include "absl/strings/str_cat.h"
22 | #include "absl/strings/string_view.h"
23 | #include "shell_encryption/prng/chacha_prng_util.h"
24 | #include "shell_encryption/status_macros.h"
25 |
26 | namespace rlwe {
27 |
28 | ChaChaPrng::ChaChaPrng(absl::string_view in_key, int position_in_buffer,
29 | int salt_counter, std::vector buffer)
30 | : key_(in_key),
31 | position_in_buffer_(position_in_buffer),
32 | salt_counter_(salt_counter),
33 | buffer_(std::move(buffer)) {}
34 |
35 | rlwe::StatusOr> ChaChaPrng::Create(
36 | absl::string_view in_key) {
37 | if (static_cast(in_key.length()) != SeedLength()) {
38 | return absl::InvalidArgumentError(
39 | absl::StrCat("Cannot create Prng with key of the "
40 | "wrong size. Real ",
41 | "key length of ", in_key.length(), " instead of expected ",
42 | "key length of ", SeedLength(), "."));
43 | }
44 | int position_in_buffer = 0;
45 | int salt_counter = 0;
46 | std::vector buffer;
47 | RLWE_RETURN_IF_ERROR(
48 | internal::ChaChaPrngResalt(in_key, internal::kChaChaOutputBytes,
49 | &salt_counter, &position_in_buffer, &buffer));
50 | return absl::WrapUnique(new ChaChaPrng(
51 | in_key, position_in_buffer, salt_counter, std::move(buffer)));
52 | }
53 |
54 | rlwe::StatusOr ChaChaPrng::Rand8() ABSL_LOCKS_EXCLUDED(mu_) {
55 | absl::MutexLock lock(mu_);
56 | return internal::ChaChaPrngRand8(key_, &position_in_buffer_, &salt_counter_,
57 | &buffer_);
58 | }
59 |
60 | rlwe::StatusOr ChaChaPrng::Rand64() ABSL_LOCKS_EXCLUDED(mu_) {
61 | absl::MutexLock lock(mu_);
62 | return internal::ChaChaPrngRand64(key_, &position_in_buffer_, &salt_counter_,
63 | &buffer_);
64 | }
65 |
66 | } // namespace rlwe
67 |
--------------------------------------------------------------------------------
/shell_encryption/prng/chacha_prng.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | // An implementation of a PRNG using the ChaCha20 stream cipher. Since this is
17 | // a stream cipher, the key stream can be obtained by "encrypting" the plaintext
18 | // 0....0.
19 |
20 | #ifndef RLWE_CHACHA_PRNG_H_
21 | #define RLWE_CHACHA_PRNG_H_
22 |
23 | #include "absl/base/thread_annotations.h"
24 | #include "absl/strings/string_view.h"
25 | #include "absl/synchronization/mutex.h"
26 | #include "shell_encryption/prng/chacha_prng_util.h"
27 | #include "shell_encryption/prng/prng.h"
28 | #include "shell_encryption/statusor.h"
29 |
30 | namespace rlwe {
31 |
32 | class ChaChaPrng : public SecurePrng {
33 | public:
34 | // Constructs a secure pseudorandom number generator using the ChaCha20 stream
35 | // cipher. The parameter in_key is the key for the ChaCha20.
36 | //
37 | // Input keys should contain sufficient randomness (such as those generated by
38 | // the ChaChaPrngGenerateKey function) to ensure the random generated strings
39 | // are pseudorandom. As long as the initial key contains sufficient entropy,
40 | // there is no bound on the number of pseudorandom bytes that can be created.
41 | //
42 | // ChaChaPrng allows replaying pseudorandom outputs. For any fixed input key,
43 | // the pseudorandom outputs of ChaChaPrng will be identical.
44 | //
45 | // For a fixed key and salt, the underlying ChaCha primitive can
46 | // generate 2^32 * 64 pseudorandom bytes. Instead, we will construct a smaller
47 | // pool of 255 * 32 bytes to match the Hkdf Prng. Once, these bytes have been
48 | // exhausted, the prng deterministically re-salts the key using a salting
49 | // counter, thereby constructing a new internal ChaCha that can output more
50 | // pseudorandom bytes.
51 | //
52 | // Fails if the key is not the expected size or on internal cryptographic
53 | // errors.
54 | //
55 | // Thread safe.
56 | static rlwe::StatusOr>
57 | Create(absl::string_view in_key);
58 |
59 | // Returns 8 bits of randomness.
60 | //
61 | // Fails on internal cryptographic errors.
62 | rlwe::StatusOr Rand8() override;
63 |
64 | // Returns 64 bits of randomness.
65 | //
66 | // Fails on internal cryptographic errors.
67 | rlwe::StatusOr Rand64() override;
68 |
69 | // Generate a valid seed for the Prng.
70 | //
71 | // Fails on internal cryptographic errors.
72 | static rlwe::StatusOr GenerateSeed() {
73 | return internal::ChaChaPrngGenerateKey();
74 | }
75 |
76 | // Output the size of the expected generated seed.
77 | static int SeedLength() { return internal::kChaChaKeyBytesSize; }
78 |
79 | private:
80 | explicit ChaChaPrng(absl::string_view in_key, int position_in_buffer,
81 | int salt_counter, std::vector buffer);
82 |
83 | absl::Mutex mu_; // Guards all values below
84 |
85 | const std::string key_;
86 | int position_in_buffer_ ABSL_GUARDED_BY(mu_);
87 | int salt_counter_ ABSL_GUARDED_BY(mu_);
88 | std::vector buffer_ ABSL_GUARDED_BY(mu_);
89 | };
90 |
91 | } // namespace rlwe
92 |
93 | #endif // RLWE_CHACHA_PRNG_H_
94 |
--------------------------------------------------------------------------------
/shell_encryption/prng/chacha_prng_test.cc:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * https://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | // This file includes specific unit tests files to test populating the buffer
17 | // every internal::kChaChaOutputBytes bytes.
18 |
19 | #include "shell_encryption/prng/chacha_prng.h"
20 |
21 | #include
22 | #include
23 | #include "absl/strings/str_cat.h"
24 | #include "shell_encryption/testing/status_matchers.h"
25 | #include "shell_encryption/testing/status_testing.h"
26 |
27 | namespace rlwe {
28 |
29 | namespace {
30 |
31 | using ::testing::Eq;
32 | using ::testing::Not;
33 |
34 | class ChaChaPrngTest : public ::testing::Test {
35 | protected:
36 | void SetUp() override {
37 | ASSERT_OK_AND_ASSIGN(seed_, ChaChaPrng::GenerateSeed());
38 | ASSERT_OK_AND_ASSIGN(prng_, ChaChaPrng::Create(seed_));
39 | }
40 |
41 | std::string seed_;
42 | std::unique_ptr prng_;
43 | };
44 |
45 | TEST_F(ChaChaPrngTest, TestRand8BeforeAndAfterResalting) {
46 | // Two random 8 bit strings have 1/256 probability of being equal. Instead,
47 | // we check that a sequence of 8 strings from the PRNG before and after
48 | // salting are not all equal.
49 | std::vector> before_resalt(internal::kChaChaOutputBytes);
50 | for (int i = 0; i < internal::kChaChaOutputBytes / 8; ++i) {
51 | SCOPED_TRACE(absl::StrCat("Iteration ", i, "."));
52 | std::vector rand8s;
53 | for (int j = 0; j < 8; ++j) {
54 | ASSERT_OK_AND_ASSIGN(auto elt, prng_->Rand8());
55 | rand8s.push_back(elt);
56 | }
57 | before_resalt.push_back(rand8s);
58 | }
59 | for (int i = 0; i < internal::kChaChaOutputBytes / 8; ++i) {
60 | SCOPED_TRACE(absl::StrCat("Iteration ", i, "."));
61 | std::vector rand8s;
62 | for (int j = 0; j < 8; ++j) {
63 | ASSERT_OK_AND_ASSIGN(auto elt, prng_->Rand8());
64 | rand8s.push_back(elt);
65 | }
66 | EXPECT_THAT(rand8s, Not(Eq(before_resalt[i])));
67 | }
68 | }
69 |
70 | TEST_F(ChaChaPrngTest, TestRand64BeforeAndAfterResalting) {
71 | std::vector before_resalt(internal::kChaChaOutputBytes / 8);
72 | for (int i = 0; i < internal::kChaChaOutputBytes / 8; ++i) {
73 | SCOPED_TRACE(absl::StrCat("Iteration ", i, "."));
74 | ASSERT_OK_AND_ASSIGN(before_resalt[i], prng_->Rand64());
75 | }
76 | for (int i = 0; i < internal::kChaChaOutputBytes / 8; ++i) {
77 | SCOPED_TRACE(absl::StrCat("Iteration ", i, "."));
78 | ASSERT_OK_AND_ASSIGN(auto r64, prng_->Rand64());
79 | EXPECT_NE(before_resalt[i], r64);
80 | }
81 | }
82 |
83 | TEST_F(ChaChaPrngTest, TestRand64WithResaltingInBetween) {
84 | for (int i = 0; i < internal::kChaChaOutputBytes - 1; ++i) {
85 | SCOPED_TRACE(absl::StrCat("Iteration ", i, "."));
86 | EXPECT_OK(prng_->Rand8());
87 | }
88 | EXPECT_OK(prng_->Rand64());
89 | }
90 |
91 | } // namespace
92 | } // namespace rlwe
93 |
--------------------------------------------------------------------------------
/shell_encryption/prng/chacha_prng_util.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include "shell_encryption/prng/chacha_prng_util.h"
16 |
17 | #include
18 | #include
19 |
20 | #include "absl/memory/memory.h"
21 | #include "absl/strings/str_cat.h"
22 | #include
23 | #include
24 | #include
25 | #include "shell_encryption/status_macros.h"
26 |
27 | namespace rlwe {
28 | namespace internal {
29 |
30 | absl::Status ChaChaPrngResalt(absl::string_view key, int buffer_size,
31 | int* salt_counter, int* position_in_buffer,
32 | std::vector* buffer) {
33 | buffer->assign(buffer_size, 0);
34 |
35 | // Following https://tools.ietf.org/html/rfc7539, Sec 2.3, we create the
36 | // nonce as a kChaChaNonceSize (=12) bytes string, where the 4 first
37 | // bytes are fixed, and the next 8 bytes correspond to the counter.
38 | std::string nonce = "salt00000000";
39 | if (nonce.size() != kChaChaNonceSize) {
40 | return absl::InternalError("The salt length is incorrect.");
41 | }
42 | Uint64 counter = static_cast(*salt_counter);
43 | for (int i = 0; i < 8; i++) {
44 | nonce[4 + i] = counter & 0xFF;
45 | counter >>= 8;
46 | }
47 |
48 | // We call the CRYPTO_chacha_20() function from OpenSSL. Note that the last
49 | // parameter is a *block* counter. The salt counter needs instead to be
50 | // included in the nonce.
51 | CRYPTO_chacha_20(buffer->data(), buffer->data(), buffer->size(),
52 | reinterpret_cast(key.data()),
53 | reinterpret_cast(nonce.data()),
54 | /* counter = */ 0);
55 |
56 | ++(*salt_counter);
57 | *position_in_buffer = 0;
58 | return absl::OkStatus();
59 | }
60 |
61 | rlwe::StatusOr ChaChaPrngGenerateKey() {
62 | std::unique_ptr buf(new Uint8[kChaChaKeyBytesSize]);
63 | // BoringSSL documentation says that it always returns 1; while
64 | // OpenSSL documentation says that it returns 1 on success, 0 otherwise. Check
65 | // for an error just in case.
66 | if (RAND_bytes(buf.get(), kChaChaKeyBytesSize) == 0) {
67 | return absl::InternalError("Internal error generating random PRNG key.");
68 | }
69 | return std::string(reinterpret_cast(buf.get()),
70 | kChaChaKeyBytesSize);
71 | }
72 |
73 | rlwe::StatusOr ChaChaPrngRand8(absl::string_view key,
74 | int* position_in_buffer,
75 | int* salt_counter,
76 | std::vector* buffer) {
77 | Uint8 rand;
78 | if (*position_in_buffer >= static_cast(buffer->size())) {
79 | RLWE_RETURN_IF_ERROR(ChaChaPrngResalt(key, kChaChaOutputBytes, salt_counter,
80 | position_in_buffer, buffer));
81 | }
82 | rand = buffer->at(*position_in_buffer);
83 | ++(*position_in_buffer);
84 | return rand;
85 | }
86 |
87 | rlwe::StatusOr ChaChaPrngRand64(absl::string_view key,
88 | int* position_in_buffer,
89 | int* salt_counter,
90 | std::vector* buffer) {
91 | Uint64 rand64 = 0;
92 | for (int i = 0; i < 8; ++i) {
93 | RLWE_ASSIGN_OR_RETURN(Uint8 rand8, ChaChaPrngRand8(key, position_in_buffer,
94 | salt_counter, buffer));
95 | rand64 += Uint64{rand8} << (8 * i);
96 | }
97 | return rand64;
98 | }
99 |
100 | } // namespace internal
101 | } // namespace rlwe
102 |
--------------------------------------------------------------------------------
/shell_encryption/prng/chacha_prng_util.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // An implementation of a PRNG using the ChaCha20 stream cipher. Since this is
18 | // a stream cipher, the key stream can be obtained by "encrypting" the plaintext
19 | // 0....0.
20 |
21 | #ifndef RLWE_CHACHA_PRNG_UTIL_H_
22 | #define RLWE_CHACHA_PRNG_UTIL_H_
23 |
24 | #include
25 |
26 | #include "absl/status/status.h"
27 | #include "absl/strings/string_view.h"
28 | #include "shell_encryption/integral_types.h"
29 | #include "shell_encryption/statusor.h"
30 |
31 | namespace rlwe {
32 | namespace internal {
33 |
34 | const int kChaChaKeyBytesSize = 32;
35 | const int kChaChaNonceSize = 12;
36 | const int kChaChaOutputBytes = 255 * 32;
37 |
38 | // Once pseudorandom output is exhausted, the salt is updated to construct
39 | // new pseudorandom output.
40 | absl::Status ChaChaPrngResalt(
41 | absl::string_view key, int buffer_size, int* salt_counter,
42 | int* position_in_buffer, std::vector* buffer);
43 |
44 | // Generates a secure key for instantiating an CHACHA.
45 | rlwe::StatusOr ChaChaPrngGenerateKey();
46 |
47 | // Returns 8 bits of randomness.
48 | //
49 | // Fails on internal cryptographic errors.
50 | rlwe::StatusOr ChaChaPrngRand8(
51 | absl::string_view key, int* position_in_buffer, int* salt_counter,
52 | std::vector* buffer);
53 |
54 | // Returns 64 bits of randomness.
55 | //
56 | // Fails on internal cryptographic errors.
57 | rlwe::StatusOr