symmetric_encryption_test.cc 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362
  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 "symmetric_encryption.h"
  16. #include <algorithm>
  17. #include <cstdint>
  18. #include <functional>
  19. #include <iostream>
  20. #include <random>
  21. #include <vector>
  22. #include <gmock/gmock.h>
  23. #include <gtest/gtest.h>
  24. #include "constants.h"
  25. #include "context.h"
  26. #include "montgomery.h"
  27. #include "ntt_parameters.h"
  28. #include "polynomial.h"
  29. #include "prng/integral_prng_types.h"
  30. #include "serialization.pb.h"
  31. #include "status_macros.h"
  32. #include "testing/parameters.h"
  33. #include "testing/status_matchers.h"
  34. #include "testing/status_testing.h"
  35. #include "testing/testing_prng.h"
  36. #include "testing/testing_utils.h"
  37. namespace {
  38. using ::rlwe::testing::StatusIs;
  39. using ::testing::Eq;
  40. using ::testing::HasSubstr;
  41. // Set constants.
  42. const int kTestingRounds = 10;
  43. // Tests symmetric-key encryption scheme, including the following homomorphic
  44. // operations: addition, scalar multiplication by a polynomial (absorb), and
  45. // multiplication. Substitutions are implemented in
  46. // testing/coefficient_polynomial_ciphertext.h, and SymmetricRlweKey::Substitute
  47. // and SymmetricRlweCiphertext::PowersOfS() (updated on substitution calls) are
  48. // further tested in testing/coefficient_polynomial_ciphertext_test.cc.
  49. template <typename ModularInt>
  50. class SymmetricRlweEncryptionTest : public ::testing::Test {
  51. public:
  52. // Sample a random key.
  53. rlwe::StatusOr<rlwe::SymmetricRlweKey<ModularInt>> SampleKey(
  54. const rlwe::RlweContext<ModularInt>* context) {
  55. RLWE_ASSIGN_OR_RETURN(std::string prng_seed,
  56. rlwe::SingleThreadPrng::GenerateSeed());
  57. RLWE_ASSIGN_OR_RETURN(auto prng, rlwe::SingleThreadPrng::Create(prng_seed));
  58. return rlwe::SymmetricRlweKey<ModularInt>::Sample(
  59. context->GetLogN(), context->GetVariance(), context->GetLogT(),
  60. context->GetModulusParams(), context->GetNttParams(), prng.get());
  61. }
  62. // Encrypt a plaintext.
  63. rlwe::StatusOr<rlwe::SymmetricRlweCiphertext<ModularInt>> Encrypt(
  64. const rlwe::SymmetricRlweKey<ModularInt>& key,
  65. const std::vector<typename ModularInt::Int>& plaintext,
  66. const rlwe::RlweContext<ModularInt>* context) {
  67. RLWE_ASSIGN_OR_RETURN(auto mont,
  68. rlwe::testing::ConvertToMontgomery<ModularInt>(
  69. plaintext, context->GetModulusParams()));
  70. auto plaintext_ntt = rlwe::Polynomial<ModularInt>::ConvertToNtt(
  71. mont, context->GetNttParams(), context->GetModulusParams());
  72. RLWE_ASSIGN_OR_RETURN(std::string prng_seed,
  73. rlwe::SingleThreadPrng::GenerateSeed());
  74. RLWE_ASSIGN_OR_RETURN(auto prng, rlwe::SingleThreadPrng::Create(prng_seed));
  75. return rlwe::Encrypt<ModularInt>(key, plaintext_ntt,
  76. context->GetErrorParams(), prng.get());
  77. }
  78. };
  79. TYPED_TEST_SUITE(SymmetricRlweEncryptionTest, rlwe::testing::ModularIntTypes);
  80. // Ensure that RemoveError works correctly on negative numbers for several
  81. // different values of t.
  82. TYPED_TEST(SymmetricRlweEncryptionTest, RemoveErrorNegative) {
  83. unsigned int seed = 0;
  84. for (const auto& params :
  85. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  86. ASSERT_OK_AND_ASSIGN(auto context,
  87. rlwe::RlweContext<TypeParam>::Create(params));
  88. for (int t = 2; t < 16; t++) {
  89. for (int i = 0; i < kTestingRounds; i++) {
  90. // Sample a plaintext in the range (modulus/2, modulus)
  91. typename TypeParam::Int plaintext =
  92. (rand_r(&seed) % (context->GetModulus() / 2)) +
  93. context->GetModulus() / 2 + 1;
  94. // Create a vector that exclusively contains the value "plaintext".
  95. ASSERT_OK_AND_ASSIGN(
  96. auto m_plaintext,
  97. TypeParam::ImportInt(plaintext, context->GetModulusParams()));
  98. std::vector<TypeParam> error_and_message(context->GetN(), m_plaintext);
  99. auto result = rlwe::RemoveError<TypeParam>(error_and_message,
  100. context->GetModulus(), t,
  101. context->GetModulusParams());
  102. // Compute the expected result using signed arithmetic. Derive its
  103. // negative equivalent by subtracting out testing::kModulus and taking
  104. // that negative value (mod t).
  105. absl::int128 expected =
  106. (static_cast<absl::int128>(plaintext) -
  107. static_cast<absl::int128>(context->GetModulus())) %
  108. t;
  109. // Finally, turn any negative values into their positive equivalents
  110. // (mod t).
  111. if (expected < 0) {
  112. expected += t;
  113. }
  114. for (unsigned int j = 0; j < context->GetN(); j++) {
  115. EXPECT_EQ(expected, result[j]) << t << plaintext;
  116. }
  117. }
  118. }
  119. }
  120. }
  121. // Ensure that RemoveError works correctly on positive numbers for several
  122. // different values of t.
  123. TYPED_TEST(SymmetricRlweEncryptionTest, RemoveErrorPositive) {
  124. for (const auto& params :
  125. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  126. ASSERT_OK_AND_ASSIGN(auto context,
  127. rlwe::RlweContext<TypeParam>::Create(params));
  128. unsigned int seed = 0;
  129. for (int t = 2; t < 16; t++) {
  130. for (int i = 0; i < kTestingRounds; i++) {
  131. // Sample a plaintext in the range (0, modulus/2)
  132. typename TypeParam::Int plaintext =
  133. rand_r(&seed) % (context->GetModulus() / 2);
  134. // Create a vector that exclusively contains the value "plaintext".
  135. ASSERT_OK_AND_ASSIGN(
  136. auto m_plaintext,
  137. TypeParam::ImportInt(plaintext, context->GetModulusParams()));
  138. std::vector<TypeParam> error_and_message(context->GetN(), m_plaintext);
  139. auto result = rlwe::RemoveError<TypeParam>(error_and_message,
  140. context->GetModulus(), t,
  141. context->GetModulusParams());
  142. for (unsigned int j = 0; j < context->GetN(); j++) {
  143. EXPECT_EQ(plaintext % t, result[j]);
  144. }
  145. }
  146. }
  147. }
  148. }
  149. // Ensure that the encryption scheme can decrypt its own ciphertexts.
  150. TYPED_TEST(SymmetricRlweEncryptionTest, CanDecrypt) {
  151. for (const auto& params :
  152. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  153. ASSERT_OK_AND_ASSIGN(auto context,
  154. rlwe::RlweContext<TypeParam>::Create(params));
  155. for (unsigned int i = 0; i < kTestingRounds; i++) {
  156. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  157. auto plaintext = rlwe::testing::SamplePlaintext<TypeParam>(
  158. context->GetN(), context->GetT());
  159. ASSERT_OK_AND_ASSIGN(auto ciphertext,
  160. this->Encrypt(key, plaintext, context.get()));
  161. ASSERT_OK_AND_ASSIGN(auto decrypted,
  162. rlwe::Decrypt<TypeParam>(key, ciphertext));
  163. EXPECT_EQ(plaintext, decrypted);
  164. }
  165. }
  166. }
  167. // Accessing out of bounds raises errors
  168. TYPED_TEST(SymmetricRlweEncryptionTest, OutOfBoundsIndex) {
  169. for (const auto& params :
  170. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  171. ASSERT_OK_AND_ASSIGN(auto context,
  172. rlwe::RlweContext<TypeParam>::Create(params));
  173. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  174. auto plaintext = rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  175. context->GetT());
  176. ASSERT_OK_AND_ASSIGN(auto ciphertext,
  177. this->Encrypt(key, plaintext, context.get()));
  178. ASSERT_OK(ciphertext.Component(ciphertext.Len() - 1));
  179. EXPECT_THAT(ciphertext.Component(ciphertext.Len()),
  180. StatusIs(::absl::StatusCode::kInvalidArgument,
  181. HasSubstr("Index out of range.")));
  182. EXPECT_THAT(ciphertext.Component(-1),
  183. StatusIs(::absl::StatusCode::kInvalidArgument,
  184. HasSubstr("Index out of range.")));
  185. }
  186. }
  187. // Check that the HE scheme is additively homomorphic.
  188. TYPED_TEST(SymmetricRlweEncryptionTest, AdditivelyHomomorphic) {
  189. for (const auto& params :
  190. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  191. ASSERT_OK_AND_ASSIGN(auto context,
  192. rlwe::RlweContext<TypeParam>::Create(params));
  193. for (unsigned int i = 0; i < kTestingRounds; i++) {
  194. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  195. std::vector<typename TypeParam::Int> plaintext1 =
  196. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  197. context->GetT());
  198. std::vector<typename TypeParam::Int> plaintext2 =
  199. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  200. context->GetT());
  201. ASSERT_OK_AND_ASSIGN(auto ciphertext1,
  202. this->Encrypt(key, plaintext1, context.get()));
  203. ASSERT_OK_AND_ASSIGN(auto ciphertext2,
  204. this->Encrypt(key, plaintext2, context.get()));
  205. ASSERT_OK_AND_ASSIGN(auto ciphertext_add, ciphertext1 + ciphertext2);
  206. ASSERT_OK_AND_ASSIGN(auto ciphertext_sub, ciphertext1 - ciphertext2);
  207. ASSERT_OK_AND_ASSIGN(std::vector<typename TypeParam::Int> decrypted_add,
  208. rlwe::Decrypt<TypeParam>(key, ciphertext_add));
  209. ASSERT_OK_AND_ASSIGN(std::vector<typename TypeParam::Int> decrypted_sub,
  210. rlwe::Decrypt<TypeParam>(key, ciphertext_sub));
  211. for (unsigned int j = 0; j < plaintext1.size(); j++) {
  212. EXPECT_EQ((plaintext1[j] + plaintext2[j]) % context->GetT(),
  213. decrypted_add[j]);
  214. EXPECT_EQ(
  215. (context->GetT() + plaintext1[j] - plaintext2[j]) % context->GetT(),
  216. decrypted_sub[j]);
  217. // Check that the error grows additively.
  218. EXPECT_EQ(ciphertext_add.Error(),
  219. ciphertext1.Error() + ciphertext2.Error());
  220. EXPECT_EQ(ciphertext_sub.Error(),
  221. ciphertext1.Error() + ciphertext2.Error());
  222. }
  223. }
  224. }
  225. }
  226. // Check that homomorphic addition can be performed in place.
  227. TYPED_TEST(SymmetricRlweEncryptionTest, AddHomomorphicallyInPlace) {
  228. for (const auto& params :
  229. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  230. ASSERT_OK_AND_ASSIGN(auto context,
  231. rlwe::RlweContext<TypeParam>::Create(params));
  232. for (unsigned int i = 0; i < kTestingRounds; i++) {
  233. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  234. std::vector<typename TypeParam::Int> plaintext1 =
  235. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  236. context->GetT());
  237. std::vector<typename TypeParam::Int> plaintext2 =
  238. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  239. context->GetT());
  240. ASSERT_OK_AND_ASSIGN(auto ciphertext1_add,
  241. this->Encrypt(key, plaintext1, context.get()));
  242. ASSERT_OK_AND_ASSIGN(auto ciphertext1_sub,
  243. this->Encrypt(key, plaintext1, context.get()));
  244. ASSERT_OK_AND_ASSIGN(auto ciphertext2,
  245. this->Encrypt(key, plaintext2, context.get()));
  246. const double ciphertext1_add_error = ciphertext1_add.Error();
  247. const double ciphertext1_sub_error = ciphertext1_sub.Error();
  248. ASSERT_OK(ciphertext1_add.AddInPlace(ciphertext2));
  249. ASSERT_OK(ciphertext1_sub.SubInPlace(ciphertext2));
  250. ASSERT_OK_AND_ASSIGN(std::vector<typename TypeParam::Int> decrypted1_add,
  251. rlwe::Decrypt<TypeParam>(key, ciphertext1_add));
  252. ASSERT_OK_AND_ASSIGN(std::vector<typename TypeParam::Int> decrypted1_sub,
  253. rlwe::Decrypt<TypeParam>(key, ciphertext1_sub));
  254. for (unsigned int j = 0; j < plaintext1.size(); j++) {
  255. EXPECT_EQ((plaintext1[j] + plaintext2[j]) % context->GetT(),
  256. decrypted1_add[j]);
  257. EXPECT_EQ(
  258. (context->GetT() + plaintext1[j] - plaintext2[j]) % context->GetT(),
  259. decrypted1_sub[j]);
  260. // Check that the error grows additively.
  261. EXPECT_EQ(ciphertext1_add.Error(),
  262. ciphertext1_add_error + ciphertext2.Error());
  263. EXPECT_EQ(ciphertext1_sub.Error(),
  264. ciphertext1_sub_error + ciphertext2.Error());
  265. }
  266. }
  267. }
  268. }
  269. // Check that homomorphic addition to a 0-ciphertext does not change the
  270. // plaintext.
  271. TYPED_TEST(SymmetricRlweEncryptionTest, AddToZero) {
  272. for (const auto& params :
  273. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  274. ASSERT_OK_AND_ASSIGN(auto context,
  275. rlwe::RlweContext<TypeParam>::Create(params));
  276. for (unsigned int i = 0; i < kTestingRounds; i++) {
  277. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  278. std::vector<typename TypeParam::Int> plaintext =
  279. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  280. context->GetT());
  281. rlwe::SymmetricRlweCiphertext<TypeParam> ciphertext1(
  282. context->GetModulusParams(), context->GetErrorParams());
  283. ASSERT_OK_AND_ASSIGN(auto ciphertext2,
  284. this->Encrypt(key, plaintext, context.get()));
  285. ASSERT_OK_AND_ASSIGN(auto ciphertext3, ciphertext1 + ciphertext2);
  286. ASSERT_OK_AND_ASSIGN(std::vector<typename TypeParam::Int> decrypted,
  287. rlwe::Decrypt<TypeParam>(key, ciphertext3));
  288. EXPECT_EQ(plaintext, decrypted);
  289. }
  290. }
  291. }
  292. // Check that homomorphic absorption works.
  293. TYPED_TEST(SymmetricRlweEncryptionTest, Absorb) {
  294. for (const auto& params :
  295. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  296. ASSERT_OK_AND_ASSIGN(auto context,
  297. rlwe::RlweContext<TypeParam>::Create(params));
  298. for (unsigned int i = 0; i < kTestingRounds; i++) {
  299. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  300. // Create the initial plaintexts.
  301. std::vector<typename TypeParam::Int> plaintext =
  302. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  303. context->GetT());
  304. ASSERT_OK_AND_ASSIGN(auto m_plaintext,
  305. rlwe::testing::ConvertToMontgomery<TypeParam>(
  306. plaintext, context->GetModulusParams()));
  307. rlwe::Polynomial<TypeParam> plaintext_ntt =
  308. rlwe::Polynomial<TypeParam>::ConvertToNtt(
  309. m_plaintext, context->GetNttParams(),
  310. context->GetModulusParams());
  311. std::vector<typename TypeParam::Int> to_absorb =
  312. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  313. context->GetT());
  314. ASSERT_OK_AND_ASSIGN(auto m_to_absorb,
  315. rlwe::testing::ConvertToMontgomery<TypeParam>(
  316. to_absorb, context->GetModulusParams()));
  317. rlwe::Polynomial<TypeParam> to_absorb_ntt =
  318. rlwe::Polynomial<TypeParam>::ConvertToNtt(
  319. m_to_absorb, context->GetNttParams(),
  320. context->GetModulusParams());
  321. // Create our expected value.
  322. ASSERT_OK_AND_ASSIGN(
  323. rlwe::Polynomial<TypeParam> expected_ntt,
  324. plaintext_ntt.Mul(to_absorb_ntt, context->GetModulusParams()));
  325. std::vector<typename TypeParam::Int> expected =
  326. rlwe::RemoveError<TypeParam>(
  327. expected_ntt.InverseNtt(context->GetNttParams(),
  328. context->GetModulusParams()),
  329. context->GetModulus(), context->GetT(),
  330. context->GetModulusParams());
  331. // Encrypt, absorb, and decrypt.
  332. ASSERT_OK_AND_ASSIGN(auto encrypt,
  333. this->Encrypt(key, plaintext, context.get()));
  334. ASSERT_OK_AND_ASSIGN(auto ciphertext, encrypt* to_absorb_ntt);
  335. ASSERT_OK_AND_ASSIGN(std::vector<typename TypeParam::Int> decrypted,
  336. rlwe::Decrypt<TypeParam>(key, ciphertext));
  337. EXPECT_EQ(expected, decrypted);
  338. // Check that the error is the product of an encryption and a plaintext.
  339. EXPECT_EQ(ciphertext.Error(),
  340. context->GetErrorParams()->B_encryption() *
  341. context->GetErrorParams()->B_plaintext());
  342. }
  343. }
  344. }
  345. // Check that homomorphic absorption in place works.
  346. TYPED_TEST(SymmetricRlweEncryptionTest, AbsorbInPlace) {
  347. for (const auto& params :
  348. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  349. ASSERT_OK_AND_ASSIGN(auto context,
  350. rlwe::RlweContext<TypeParam>::Create(params));
  351. for (unsigned int i = 0; i < kTestingRounds; i++) {
  352. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  353. // Create the initial plaintexts.
  354. std::vector<typename TypeParam::Int> plaintext =
  355. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  356. context->GetT());
  357. ASSERT_OK_AND_ASSIGN(auto m_plaintext,
  358. rlwe::testing::ConvertToMontgomery<TypeParam>(
  359. plaintext, context->GetModulusParams()));
  360. rlwe::Polynomial<TypeParam> plaintext_ntt =
  361. rlwe::Polynomial<TypeParam>::ConvertToNtt(
  362. m_plaintext, context->GetNttParams(),
  363. context->GetModulusParams());
  364. std::vector<typename TypeParam::Int> to_absorb =
  365. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  366. context->GetT());
  367. ASSERT_OK_AND_ASSIGN(auto m_to_absorb,
  368. rlwe::testing::ConvertToMontgomery<TypeParam>(
  369. to_absorb, context->GetModulusParams()));
  370. rlwe::Polynomial<TypeParam> to_absorb_ntt =
  371. rlwe::Polynomial<TypeParam>::ConvertToNtt(
  372. m_to_absorb, context->GetNttParams(),
  373. context->GetModulusParams());
  374. // Create our expected value.
  375. ASSERT_OK_AND_ASSIGN(
  376. rlwe::Polynomial<TypeParam> expected_ntt,
  377. plaintext_ntt.Mul(to_absorb_ntt, context->GetModulusParams()));
  378. std::vector<typename TypeParam::Int> expected =
  379. rlwe::RemoveError<TypeParam>(
  380. expected_ntt.InverseNtt(context->GetNttParams(),
  381. context->GetModulusParams()),
  382. context->GetModulus(), context->GetT(),
  383. context->GetModulusParams());
  384. // Encrypt, absorb in place, and decrypt.
  385. ASSERT_OK_AND_ASSIGN(auto ciphertext,
  386. this->Encrypt(key, plaintext, context.get()));
  387. ASSERT_OK(ciphertext.AbsorbInPlace(to_absorb_ntt));
  388. ASSERT_OK_AND_ASSIGN(std::vector<typename TypeParam::Int> decrypted,
  389. rlwe::Decrypt<TypeParam>(key, ciphertext));
  390. EXPECT_EQ(expected, decrypted);
  391. // Check that the error is the product of an encryption and a plaintext.
  392. EXPECT_EQ(ciphertext.Error(),
  393. context->GetErrorParams()->B_encryption() *
  394. context->GetErrorParams()->B_plaintext());
  395. }
  396. }
  397. }
  398. // Check that homomorphic absorption of a scalar works.
  399. TYPED_TEST(SymmetricRlweEncryptionTest, AbsorbScalar) {
  400. for (const auto& params :
  401. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  402. ASSERT_OK_AND_ASSIGN(auto context,
  403. rlwe::RlweContext<TypeParam>::Create(params));
  404. unsigned int seed = 0;
  405. for (unsigned int i = 0; i < kTestingRounds; i++) {
  406. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  407. // Create the initial plaintexts.
  408. std::vector<typename TypeParam::Int> plaintext =
  409. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  410. context->GetT());
  411. ASSERT_OK_AND_ASSIGN(auto m_plaintext,
  412. rlwe::testing::ConvertToMontgomery<TypeParam>(
  413. plaintext, context->GetModulusParams()));
  414. rlwe::Polynomial<TypeParam> plaintext_ntt =
  415. rlwe::Polynomial<TypeParam>::ConvertToNtt(
  416. m_plaintext, context->GetNttParams(),
  417. context->GetModulusParams());
  418. ASSERT_OK_AND_ASSIGN(TypeParam to_absorb,
  419. TypeParam::ImportInt(rand_r(&seed) % context->GetT(),
  420. context->GetModulusParams()));
  421. // Create our expected value.
  422. ASSERT_OK_AND_ASSIGN(
  423. rlwe::Polynomial<TypeParam> expected_ntt,
  424. plaintext_ntt.Mul(to_absorb, context->GetModulusParams()));
  425. std::vector<typename TypeParam::Int> expected =
  426. rlwe::RemoveError<TypeParam>(
  427. expected_ntt.InverseNtt(context->GetNttParams(),
  428. context->GetModulusParams()),
  429. context->GetModulus(), context->GetT(),
  430. context->GetModulusParams());
  431. // Encrypt, absorb, and decrypt.
  432. ASSERT_OK_AND_ASSIGN(auto encrypt,
  433. this->Encrypt(key, plaintext, context.get()));
  434. ASSERT_OK_AND_ASSIGN(auto ciphertext, encrypt* to_absorb);
  435. ASSERT_OK_AND_ASSIGN(std::vector<typename TypeParam::Int> decrypted,
  436. rlwe::Decrypt<TypeParam>(key, ciphertext));
  437. EXPECT_EQ(expected, decrypted);
  438. // Expect the error to grow multiplicatively.
  439. EXPECT_EQ(ciphertext.Error(), context->GetErrorParams()->B_encryption() *
  440. static_cast<double>(to_absorb.ExportInt(
  441. context->GetModulusParams())));
  442. }
  443. }
  444. }
  445. // Check that homomorphic absorption of a scalar in place works.
  446. TYPED_TEST(SymmetricRlweEncryptionTest, AbsorbScalarInPlace) {
  447. for (const auto& params :
  448. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  449. ASSERT_OK_AND_ASSIGN(auto context,
  450. rlwe::RlweContext<TypeParam>::Create(params));
  451. unsigned int seed = 0;
  452. for (unsigned int i = 0; i < kTestingRounds; i++) {
  453. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  454. // Create the initial plaintexts.
  455. std::vector<typename TypeParam::Int> plaintext =
  456. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  457. context->GetT());
  458. ASSERT_OK_AND_ASSIGN(auto m_plaintext,
  459. rlwe::testing::ConvertToMontgomery<TypeParam>(
  460. plaintext, context->GetModulusParams()));
  461. rlwe::Polynomial<TypeParam> plaintext_ntt =
  462. rlwe::Polynomial<TypeParam>::ConvertToNtt(
  463. m_plaintext, context->GetNttParams(),
  464. context->GetModulusParams());
  465. ASSERT_OK_AND_ASSIGN(TypeParam to_absorb,
  466. TypeParam::ImportInt(rand_r(&seed) % context->GetT(),
  467. context->GetModulusParams()));
  468. // Create our expected value.
  469. ASSERT_OK_AND_ASSIGN(
  470. rlwe::Polynomial<TypeParam> expected_ntt,
  471. plaintext_ntt.Mul(to_absorb, context->GetModulusParams()));
  472. std::vector<typename TypeParam::Int> expected =
  473. rlwe::RemoveError<TypeParam>(
  474. expected_ntt.InverseNtt(context->GetNttParams(),
  475. context->GetModulusParams()),
  476. context->GetModulus(), context->GetT(),
  477. context->GetModulusParams());
  478. // Encrypt, absorb, and decrypt.
  479. ASSERT_OK_AND_ASSIGN(auto ciphertext,
  480. this->Encrypt(key, plaintext, context.get()));
  481. ASSERT_OK(ciphertext.AbsorbInPlace(to_absorb));
  482. ASSERT_OK_AND_ASSIGN(std::vector<typename TypeParam::Int> decrypted,
  483. rlwe::Decrypt<TypeParam>(key, ciphertext));
  484. EXPECT_EQ(expected, decrypted);
  485. // Expect the error to grow multiplicatively.
  486. EXPECT_EQ(ciphertext.Error(), context->GetErrorParams()->B_encryption() *
  487. static_cast<double>(to_absorb.ExportInt(
  488. context->GetModulusParams())));
  489. }
  490. }
  491. }
  492. // Check that we cannot multiply with an empty ciphertext.
  493. TYPED_TEST(SymmetricRlweEncryptionTest, EmptyCipherMultiplication) {
  494. for (const auto& params :
  495. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  496. ASSERT_OK_AND_ASSIGN(auto context,
  497. rlwe::RlweContext<TypeParam>::Create(params));
  498. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  499. // Create a plaintext
  500. std::vector<typename TypeParam::Int> plaintext =
  501. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  502. context->GetT());
  503. // Encrypt, multiply
  504. ASSERT_OK_AND_ASSIGN(auto ciphertext1,
  505. this->Encrypt(key, plaintext, context.get()));
  506. // empty cipher
  507. std::vector<rlwe::Polynomial<TypeParam>> c;
  508. rlwe::SymmetricRlweCiphertext<TypeParam> ciphertext2(
  509. c, 1, 0, context->GetModulusParams(), context->GetErrorParams());
  510. EXPECT_THAT(
  511. ciphertext1 * ciphertext2,
  512. StatusIs(::absl::StatusCode::kInvalidArgument,
  513. HasSubstr("Cannot multiply using an empty ciphertext.")));
  514. EXPECT_THAT(
  515. ciphertext2 * ciphertext1,
  516. StatusIs(::absl::StatusCode::kInvalidArgument,
  517. HasSubstr("Cannot multiply using an empty ciphertext.")));
  518. c.push_back(rlwe::Polynomial<TypeParam>());
  519. rlwe::SymmetricRlweCiphertext<TypeParam> ciphertext3(
  520. c, 1, 0, context->GetModulusParams(), context->GetErrorParams());
  521. EXPECT_THAT(ciphertext1 * ciphertext3,
  522. StatusIs(::absl::StatusCode::kInvalidArgument,
  523. HasSubstr("Cannot multiply using an empty polynomial "
  524. "in the ciphertext.")));
  525. EXPECT_THAT(ciphertext3 * ciphertext1,
  526. StatusIs(::absl::StatusCode::kInvalidArgument,
  527. HasSubstr("Cannot multiply using an empty polynomial "
  528. "in the ciphertext.")));
  529. }
  530. }
  531. // Check that the scheme is multiplicatively homomorphic.
  532. TYPED_TEST(SymmetricRlweEncryptionTest, MultiplicativelyHomomorphic) {
  533. if (sizeof(TypeParam) > 2) { // No multiplicative homomorphism possible when
  534. // TypeParam = Uint16
  535. for (const auto& params :
  536. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  537. ASSERT_OK_AND_ASSIGN(auto context,
  538. rlwe::RlweContext<TypeParam>::Create(params));
  539. for (int i = 0; i < kTestingRounds; i++) {
  540. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  541. // Create the initial plaintexts.
  542. std::vector<typename TypeParam::Int> plaintext1 =
  543. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  544. context->GetT());
  545. ASSERT_OK_AND_ASSIGN(auto mp1,
  546. rlwe::testing::ConvertToMontgomery<TypeParam>(
  547. plaintext1, context->GetModulusParams()));
  548. rlwe::Polynomial<TypeParam> plaintext1_ntt =
  549. rlwe::Polynomial<TypeParam>::ConvertToNtt(
  550. mp1, context->GetNttParams(), context->GetModulusParams());
  551. std::vector<typename TypeParam::Int> plaintext2 =
  552. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  553. context->GetT());
  554. ASSERT_OK_AND_ASSIGN(auto mp2,
  555. rlwe::testing::ConvertToMontgomery<TypeParam>(
  556. plaintext2, context->GetModulusParams()));
  557. rlwe::Polynomial<TypeParam> plaintext2_ntt =
  558. rlwe::Polynomial<TypeParam>::ConvertToNtt(
  559. mp2, context->GetNttParams(), context->GetModulusParams());
  560. // Encrypt, multiply, and decrypt.
  561. ASSERT_OK_AND_ASSIGN(auto ciphertext1,
  562. this->Encrypt(key, plaintext1, context.get()));
  563. ASSERT_OK_AND_ASSIGN(auto ciphertext2,
  564. this->Encrypt(key, plaintext2, context.get()));
  565. ASSERT_OK_AND_ASSIGN(auto product, ciphertext1* ciphertext2);
  566. ASSERT_OK_AND_ASSIGN(std::vector<typename TypeParam::Int> decrypted,
  567. rlwe::Decrypt<TypeParam>(key, product));
  568. // Create the polynomial we expect.
  569. ASSERT_OK_AND_ASSIGN(
  570. rlwe::Polynomial<TypeParam> expected_ntt,
  571. plaintext1_ntt.Mul(plaintext2_ntt, context->GetModulusParams()));
  572. std::vector<typename TypeParam::Int> expected =
  573. rlwe::RemoveError<TypeParam>(
  574. expected_ntt.InverseNtt(context->GetNttParams(),
  575. context->GetModulusParams()),
  576. context->GetModulus(), context->GetT(),
  577. context->GetModulusParams());
  578. EXPECT_EQ(expected, decrypted);
  579. // Expect that the error grows multiplicatively.
  580. EXPECT_EQ(product.Error(), ciphertext1.Error() * ciphertext2.Error());
  581. }
  582. }
  583. }
  584. }
  585. // Check that many homomorphic additions can be performed.
  586. TYPED_TEST(SymmetricRlweEncryptionTest, ManyHomomorphicAdds) {
  587. for (const auto& params :
  588. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  589. ASSERT_OK_AND_ASSIGN(auto context,
  590. rlwe::RlweContext<TypeParam>::Create(params));
  591. // Sample a starting plaintext and ciphertext and create aggregators;
  592. std::vector<typename TypeParam::Int> plaintext =
  593. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  594. context->GetT());
  595. std::vector<typename TypeParam::Int> plaintext_sum = plaintext;
  596. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  597. ASSERT_OK_AND_ASSIGN(auto ciphertext_sum,
  598. this->Encrypt(key, plaintext, context.get()));
  599. // Sample a fresh plaintext.
  600. plaintext = rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  601. context->GetT());
  602. ASSERT_OK_AND_ASSIGN(auto ciphertext,
  603. this->Encrypt(key, plaintext, context.get()));
  604. int num_adds = 50;
  605. // Perform 50 homomorphic ciphertext additions with the fresh ciphertext.
  606. for (int j = 0; j < num_adds; j++) {
  607. // Add the new plaintext to the old plaintext.
  608. for (unsigned int k = 0; k < context->GetN(); k++) {
  609. plaintext_sum[k] += plaintext[k];
  610. plaintext_sum[k] %= context->GetT();
  611. }
  612. // Add the new ciphertext to the old ciphertext.
  613. ASSERT_OK_AND_ASSIGN(ciphertext_sum, ciphertext_sum + ciphertext);
  614. }
  615. ASSERT_OK_AND_ASSIGN(std::vector<typename TypeParam::Int> decrypted,
  616. rlwe::Decrypt<TypeParam>(key, ciphertext_sum));
  617. // Ensure the values are the same.
  618. EXPECT_EQ(plaintext_sum, decrypted);
  619. // Expect that the ciphertext sum's error grows by the additively by the
  620. // ciphertext's error.
  621. EXPECT_GT(ciphertext_sum.Error(), num_adds * ciphertext.Error());
  622. }
  623. }
  624. // Check that ciphertext deserialization cannot handle more than
  625. // rlwe::kMaxNumCoeffs coefficients.
  626. TYPED_TEST(SymmetricRlweEncryptionTest,
  627. ExceedMaxNumCoeffDeserializeCiphertext) {
  628. for (const auto& params :
  629. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  630. ASSERT_OK_AND_ASSIGN(auto context,
  631. rlwe::RlweContext<TypeParam>::Create(params));
  632. int num_coeffs = rlwe::kMaxNumCoeffs + 1;
  633. std::vector<rlwe::Polynomial<TypeParam>> c;
  634. for (int i = 0; i < num_coeffs; i++) {
  635. c.push_back(rlwe::Polynomial<TypeParam>(1, context->GetModulusParams()));
  636. }
  637. rlwe::SymmetricRlweCiphertext<TypeParam> ciphertext(
  638. c, 1, 0, context->GetModulusParams(), context->GetErrorParams());
  639. // Serialize and deserialize.
  640. ASSERT_OK_AND_ASSIGN(rlwe::SerializedSymmetricRlweCiphertext serialized,
  641. ciphertext.Serialize());
  642. EXPECT_THAT(
  643. rlwe::SymmetricRlweCiphertext<TypeParam>::Deserialize(
  644. serialized, context->GetModulusParams(), context->GetErrorParams()),
  645. StatusIs(::absl::StatusCode::kInvalidArgument,
  646. HasSubstr(absl::StrCat(
  647. "Number of coefficients, ", serialized.c_size(),
  648. ", cannot be more than ", rlwe::kMaxNumCoeffs, "."))));
  649. }
  650. }
  651. // Check that ciphertext serialization works.
  652. TYPED_TEST(SymmetricRlweEncryptionTest, SerializeCiphertext) {
  653. for (const auto& params :
  654. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  655. ASSERT_OK_AND_ASSIGN(auto context,
  656. rlwe::RlweContext<TypeParam>::Create(params));
  657. for (int i = 0; i < kTestingRounds; i++) {
  658. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  659. std::vector<typename TypeParam::Int> plaintext =
  660. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  661. context->GetT());
  662. ASSERT_OK_AND_ASSIGN(auto ciphertext,
  663. this->Encrypt(key, plaintext, context.get()));
  664. // Serialize and deserialize.
  665. ASSERT_OK_AND_ASSIGN(rlwe::SerializedSymmetricRlweCiphertext serialized,
  666. ciphertext.Serialize());
  667. ASSERT_OK_AND_ASSIGN(
  668. auto deserialized,
  669. rlwe::SymmetricRlweCiphertext<TypeParam>::Deserialize(
  670. serialized, context->GetModulusParams(),
  671. context->GetErrorParams()));
  672. // Decrypt and check equality.
  673. ASSERT_OK_AND_ASSIGN(
  674. std::vector<typename TypeParam::Int> deserialized_plaintext,
  675. rlwe::Decrypt<TypeParam>(key, deserialized));
  676. EXPECT_EQ(plaintext, deserialized_plaintext);
  677. // Check that the error stays the same.
  678. EXPECT_EQ(deserialized.Error(), ciphertext.Error());
  679. }
  680. }
  681. }
  682. // Check that key serialization works.
  683. TYPED_TEST(SymmetricRlweEncryptionTest, SerializeKey) {
  684. for (const auto& params :
  685. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  686. ASSERT_OK_AND_ASSIGN(auto context,
  687. rlwe::RlweContext<TypeParam>::Create(params));
  688. for (int i = 0; i < kTestingRounds; i++) {
  689. ASSERT_OK_AND_ASSIGN(auto original_key, this->SampleKey(context.get()));
  690. std::vector<typename TypeParam::Int> plaintext =
  691. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  692. context->GetT());
  693. // Serialize key, deserialize, and ensure the deserialized key is
  694. // interoperable with the original key.
  695. ASSERT_OK_AND_ASSIGN(rlwe::SerializedNttPolynomial serialized,
  696. original_key.Serialize());
  697. ASSERT_OK_AND_ASSIGN(
  698. auto deserialized_key,
  699. rlwe::SymmetricRlweKey<TypeParam>::Deserialize(
  700. context->GetVariance(), context->GetLogT(), serialized,
  701. context->GetModulusParams(), context->GetNttParams()));
  702. // Test that a ciphertext encrypted with the original key decrypts under
  703. // the deserialized key.
  704. ASSERT_OK_AND_ASSIGN(
  705. auto ekey1, this->Encrypt(original_key, plaintext, context.get()));
  706. ASSERT_OK_AND_ASSIGN(auto dkey1,
  707. rlwe::Decrypt<TypeParam>(deserialized_key, ekey1));
  708. EXPECT_EQ(dkey1, plaintext);
  709. // Test that a ciphertext encrypted with the deserialized key decrypts
  710. // under the original key.
  711. ASSERT_OK_AND_ASSIGN(auto ekey2, this->Encrypt(deserialized_key,
  712. plaintext, context.get()));
  713. ASSERT_OK_AND_ASSIGN(auto dkey2,
  714. rlwe::Decrypt<TypeParam>(original_key, ekey2));
  715. EXPECT_EQ(dkey2, plaintext);
  716. }
  717. }
  718. }
  719. // Try an ill-formed key modulus switching
  720. TYPED_TEST(SymmetricRlweEncryptionTest, FailingKeyModulusReduction) {
  721. for (const auto& params :
  722. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  723. ASSERT_OK_AND_ASSIGN(auto context,
  724. rlwe::RlweContext<TypeParam>::Create(params));
  725. // p is the original modulus and q is the new modulus we want to switch to
  726. // For modulus switching, p % t must be equal to q % t, where t is the
  727. // plaintext modulus, and both need to be congruent to 1 mod 2n.
  728. typename TypeParam::Int p = context->GetModulus();
  729. typename TypeParam::Int q = p - (context->GetN() << 1);
  730. EXPECT_NE(q % context->GetT(), p % context->GetT());
  731. ASSERT_OK_AND_ASSIGN(auto context_q, rlwe::RlweContext<TypeParam>::Create(
  732. {.modulus = q,
  733. .log_n = params.log_n,
  734. .log_t = params.log_t,
  735. .variance = params.variance}));
  736. ASSERT_OK_AND_ASSIGN(auto key_p, this->SampleKey(context.get()));
  737. auto status = key_p.template SwitchModulus<TypeParam>(
  738. context_q->GetModulusParams(), context_q->GetNttParams());
  739. EXPECT_THAT(status, StatusIs(::absl::StatusCode::kInvalidArgument,
  740. HasSubstr("p % t != q % t")));
  741. }
  742. }
  743. // Try an ill-formed ciphertext modulus switching
  744. TYPED_TEST(SymmetricRlweEncryptionTest, FailingCiphertextModulusReduction) {
  745. for (const auto& params :
  746. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  747. ASSERT_OK_AND_ASSIGN(auto context,
  748. rlwe::RlweContext<TypeParam>::Create(params));
  749. // p is the original modulus and q is the new modulus we want to switch to
  750. // For modulus switching, p % t must be equal to q % t, where t is the
  751. // plaintext modulus, and both need to be congruent to 1 mod 2n.
  752. typename TypeParam::Int p = context->GetModulus();
  753. typename TypeParam::Int q = p - (context->GetN() << 1);
  754. EXPECT_NE(q % context->GetT(), p % context->GetT());
  755. ASSERT_OK_AND_ASSIGN(auto context_q, rlwe::RlweContext<TypeParam>::Create(
  756. {.modulus = q,
  757. .log_n = params.log_n,
  758. .log_t = params.log_t,
  759. .variance = params.variance}));
  760. ASSERT_OK_AND_ASSIGN(auto key_p, this->SampleKey(context.get()));
  761. // sample ciphertext modulo p.
  762. auto plaintext = rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  763. context->GetT());
  764. ASSERT_OK_AND_ASSIGN(auto ciphertext,
  765. this->Encrypt(key_p, plaintext, context.get()));
  766. auto status = ciphertext.template SwitchModulus<TypeParam>(
  767. context->GetNttParams(), context_q->GetModulusParams(),
  768. context_q->GetNttParams(), context_q->GetErrorParams(),
  769. context->GetT());
  770. EXPECT_THAT(status, StatusIs(::absl::StatusCode::kInvalidArgument,
  771. HasSubstr("p % t != q % t")));
  772. }
  773. }
  774. // Test modulus switching.
  775. TYPED_TEST(SymmetricRlweEncryptionTest, ModulusReduction) {
  776. for (const auto& params :
  777. rlwe::testing::ContextParametersModulusSwitching<TypeParam>::Value()) {
  778. auto params1 = std::get<0>(params), params2 = std::get<1>(params);
  779. ASSERT_OK_AND_ASSIGN(auto context1,
  780. rlwe::RlweContext<TypeParam>::Create(params1));
  781. ASSERT_OK_AND_ASSIGN(auto context2,
  782. rlwe::RlweContext<TypeParam>::Create(params2));
  783. for (int i = 0; i < kTestingRounds; i++) {
  784. // Create a key.
  785. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context1.get()));
  786. ASSERT_OK_AND_ASSIGN(
  787. auto key_switched,
  788. key.template SwitchModulus<TypeParam>(context2->GetModulusParams(),
  789. context2->GetNttParams()));
  790. // Create a plaintext.
  791. std::vector<typename TypeParam::Int> plaintext =
  792. rlwe::testing::SamplePlaintext<TypeParam>(context1->GetN(),
  793. context1->GetT());
  794. ASSERT_OK_AND_ASSIGN(auto ciphertext,
  795. this->Encrypt(key, plaintext, context1.get()));
  796. // Switch moduli.
  797. ASSERT_OK_AND_ASSIGN(
  798. auto ciphertext_switched,
  799. ciphertext.template SwitchModulus<TypeParam>(
  800. context1->GetNttParams(), context2->GetModulusParams(),
  801. context2->GetNttParams(), context2->GetErrorParams(),
  802. context2->GetT()));
  803. // Decrypt in the smaller modulus.
  804. ASSERT_OK_AND_ASSIGN(
  805. auto decrypted,
  806. rlwe::Decrypt<TypeParam>(key_switched, ciphertext_switched));
  807. EXPECT_EQ(plaintext, decrypted);
  808. }
  809. }
  810. }
  811. // Check that modulus switching reduces the error.
  812. TYPED_TEST(SymmetricRlweEncryptionTest, ModulusSwitchingReducesLargeError) {
  813. for (const auto& params :
  814. rlwe::testing::ContextParametersModulusSwitching<TypeParam>::Value()) {
  815. auto params1 = std::get<0>(params), params2 = std::get<1>(params);
  816. ASSERT_OK_AND_ASSIGN(auto context1,
  817. rlwe::RlweContext<TypeParam>::Create(params1));
  818. ASSERT_OK_AND_ASSIGN(auto context2,
  819. rlwe::RlweContext<TypeParam>::Create(params2));
  820. for (int i = 0; i < kTestingRounds; i++) {
  821. // Create a key.
  822. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context1.get()));
  823. ASSERT_OK_AND_ASSIGN(
  824. auto key_switched,
  825. key.template SwitchModulus<TypeParam>(context2->GetModulusParams(),
  826. context2->GetNttParams()));
  827. // Create a plaintext.
  828. std::vector<typename TypeParam::Int> plaintext =
  829. rlwe::testing::SamplePlaintext<TypeParam>(context1->GetN(),
  830. context1->GetT());
  831. ASSERT_OK_AND_ASSIGN(auto ciphertext,
  832. this->Encrypt(key, plaintext, context1.get()));
  833. // Square the ciphertext
  834. ASSERT_OK_AND_ASSIGN(auto squared, ciphertext* ciphertext);
  835. // Switch moduli.
  836. ASSERT_OK_AND_ASSIGN(
  837. auto squared_switched,
  838. squared.template SwitchModulus<TypeParam>(
  839. context1->GetNttParams(), context2->GetModulusParams(),
  840. context2->GetNttParams(), context2->GetErrorParams(),
  841. context2->GetT()));
  842. // Decrypt
  843. ASSERT_OK_AND_ASSIGN(auto squared_decrypted,
  844. rlwe::Decrypt<TypeParam>(key, squared));
  845. ASSERT_OK_AND_ASSIGN(
  846. auto squared_switched_decrypted,
  847. rlwe::Decrypt<TypeParam>(key_switched, squared_switched));
  848. EXPECT_EQ(squared_decrypted, squared_switched_decrypted);
  849. // Expect that the error reduces after a modulus switch when the error is
  850. // large.
  851. EXPECT_LT(squared_switched.Error(), squared.Error());
  852. // But that the error doesn't reduce when the error is small.
  853. EXPECT_GT(squared_switched.Error(), ciphertext.Error());
  854. }
  855. }
  856. }
  857. // Check that we cannot perform operations between ciphertexts encrypted under
  858. // different powers of s.
  859. TYPED_TEST(SymmetricRlweEncryptionTest, OperationsFailOnMismatchedPowersOfS) {
  860. for (const auto& params :
  861. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  862. ASSERT_OK_AND_ASSIGN(auto context,
  863. rlwe::RlweContext<TypeParam>::Create(params));
  864. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  865. std::vector<typename TypeParam::Int> plaintext1 =
  866. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  867. context->GetT());
  868. ASSERT_OK_AND_ASSIGN(auto m1, rlwe::testing::ConvertToMontgomery<TypeParam>(
  869. plaintext1, context->GetModulusParams()));
  870. auto plaintext1_ntt = rlwe::Polynomial<TypeParam>::ConvertToNtt(
  871. m1, context->GetNttParams(), context->GetModulusParams());
  872. std::vector<typename TypeParam::Int> plaintext2 =
  873. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  874. context->GetT());
  875. auto ciphertext1 = rlwe::SymmetricRlweCiphertext<TypeParam>(
  876. {plaintext1_ntt}, 1, context->GetErrorParams()->B_encryption(),
  877. context->GetModulusParams(), context->GetErrorParams());
  878. auto ciphertext2 = rlwe::SymmetricRlweCiphertext<TypeParam>(
  879. {plaintext1_ntt}, 2, context->GetErrorParams()->B_encryption(),
  880. context->GetModulusParams(), context->GetErrorParams());
  881. EXPECT_THAT(ciphertext1 + ciphertext2,
  882. StatusIs(::absl::StatusCode::kInvalidArgument,
  883. HasSubstr("must be encrypted with the same key")));
  884. EXPECT_THAT(ciphertext1 * ciphertext2,
  885. StatusIs(::absl::StatusCode::kInvalidArgument,
  886. HasSubstr("must be encrypted with the same key")));
  887. }
  888. }
  889. // Verifies that the power of S changes as expected in adds / mults.
  890. TYPED_TEST(SymmetricRlweEncryptionTest, AddsAndMultPreservePowerOfS) {
  891. for (const auto& params :
  892. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  893. ASSERT_OK_AND_ASSIGN(auto context,
  894. rlwe::RlweContext<TypeParam>::Create(params));
  895. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  896. std::vector<typename TypeParam::Int> plaintext1 =
  897. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  898. context->GetT());
  899. ASSERT_OK_AND_ASSIGN(auto m1, rlwe::testing::ConvertToMontgomery<TypeParam>(
  900. plaintext1, context->GetModulusParams()));
  901. auto plaintext1_ntt = rlwe::Polynomial<TypeParam>::ConvertToNtt(
  902. m1, context->GetNttParams(), context->GetModulusParams());
  903. std::vector<typename TypeParam::Int> plaintext2 =
  904. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  905. context->GetT());
  906. auto ciphertext1 = rlwe::SymmetricRlweCiphertext<TypeParam>(
  907. {plaintext1_ntt}, 2, context->GetErrorParams()->B_encryption(),
  908. context->GetModulusParams(), context->GetErrorParams());
  909. auto ciphertext2 = rlwe::SymmetricRlweCiphertext<TypeParam>(
  910. {plaintext1_ntt}, 2, context->GetErrorParams()->B_encryption(),
  911. context->GetModulusParams(), context->GetErrorParams());
  912. EXPECT_EQ(ciphertext1.PowerOfS(), 2);
  913. EXPECT_EQ(ciphertext2.PowerOfS(), 2);
  914. ASSERT_OK_AND_ASSIGN(auto sum, ciphertext1 + ciphertext2);
  915. EXPECT_EQ(sum.PowerOfS(), 2);
  916. ASSERT_OK_AND_ASSIGN(auto prod, ciphertext1* ciphertext2);
  917. EXPECT_EQ(prod.PowerOfS(), 2);
  918. }
  919. }
  920. // Check that substitutions of the form 2^k + 1 work.
  921. TYPED_TEST(SymmetricRlweEncryptionTest, Substitutes) {
  922. for (const auto& params :
  923. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  924. ASSERT_OK_AND_ASSIGN(auto context,
  925. rlwe::RlweContext<TypeParam>::Create(params));
  926. for (int k = 1; k < context->GetLogN(); k++) {
  927. int substitution_power = (1 << k) + 1;
  928. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  929. auto plaintext = rlwe::testing::SamplePlaintext<TypeParam>(
  930. context->GetN(), context->GetT());
  931. // Create the expected polynomial output by substituting the plaintext.
  932. ASSERT_OK_AND_ASSIGN(auto m_plaintext,
  933. rlwe::testing::ConvertToMontgomery<TypeParam>(
  934. plaintext, context->GetModulusParams()));
  935. auto plaintext_ntt = rlwe::Polynomial<TypeParam>::ConvertToNtt(
  936. m_plaintext, context->GetNttParams(), context->GetModulusParams());
  937. ASSERT_OK_AND_ASSIGN(
  938. auto expected_ntt,
  939. plaintext_ntt.Substitute(substitution_power, context->GetNttParams(),
  940. context->GetModulusParams()));
  941. std::vector<typename TypeParam::Int> expected =
  942. rlwe::RemoveError<TypeParam>(
  943. expected_ntt.InverseNtt(context->GetNttParams(),
  944. context->GetModulusParams()),
  945. context->GetModulus(), context->GetT(),
  946. context->GetModulusParams());
  947. // Encrypt and substitute the ciphertext. Decrypt with a substituted key.
  948. ASSERT_OK_AND_ASSIGN(auto ciphertext,
  949. this->Encrypt(key, plaintext, context.get()));
  950. ASSERT_OK_AND_ASSIGN(
  951. auto substituted,
  952. ciphertext.Substitute(substitution_power, context->GetNttParams()));
  953. ASSERT_OK_AND_ASSIGN(auto key_sub, key.Substitute(substitution_power));
  954. ASSERT_OK_AND_ASSIGN(std::vector<typename TypeParam::Int> decrypted,
  955. rlwe::Decrypt<TypeParam>(key_sub, substituted));
  956. EXPECT_EQ(decrypted, expected);
  957. EXPECT_EQ(substituted.PowerOfS(), substitution_power);
  958. EXPECT_EQ(substituted.Error(), ciphertext.Error());
  959. }
  960. }
  961. }
  962. // Check that substitution of 2 does not work.
  963. TYPED_TEST(SymmetricRlweEncryptionTest, SubstitutionFailsOnEvenPower) {
  964. for (const auto& params :
  965. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  966. ASSERT_OK_AND_ASSIGN(auto context,
  967. rlwe::RlweContext<TypeParam>::Create(params));
  968. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  969. auto plaintext = rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  970. context->GetT());
  971. ASSERT_OK_AND_ASSIGN(auto enc,
  972. this->Encrypt(key, plaintext, context.get()));
  973. EXPECT_THAT(
  974. enc.Substitute(2, context->GetNttParams()),
  975. StatusIs(::absl::StatusCode::kInvalidArgument,
  976. HasSubstr("power must be a non-negative odd integer")));
  977. }
  978. }
  979. // Check that the power of s updates after several substitutions.
  980. TYPED_TEST(SymmetricRlweEncryptionTest, PowerOfSUpdatedAfterRepeatedSubs) {
  981. for (const auto& params :
  982. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  983. ASSERT_OK_AND_ASSIGN(auto context,
  984. rlwe::RlweContext<TypeParam>::Create(params));
  985. int substitution_power = 5;
  986. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  987. auto plaintext = rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  988. context->GetT());
  989. // Encrypt and substitute the ciphertext. Decrypt with a substituted key.
  990. ASSERT_OK_AND_ASSIGN(auto ciphertext1,
  991. this->Encrypt(key, plaintext, context.get()));
  992. ASSERT_OK_AND_ASSIGN(
  993. auto ciphertext2,
  994. ciphertext1.Substitute(substitution_power, context->GetNttParams()));
  995. ASSERT_OK_AND_ASSIGN(
  996. auto ciphertext3,
  997. ciphertext2.Substitute(substitution_power, context->GetNttParams()));
  998. EXPECT_EQ(ciphertext3.PowerOfS(),
  999. (substitution_power * substitution_power) % (2 * key.Len()));
  1000. }
  1001. }
  1002. // Check that operations can only be performed when powers of s match.
  1003. TYPED_TEST(SymmetricRlweEncryptionTest, PowersOfSMustMatchOnOperations) {
  1004. for (const auto& params :
  1005. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  1006. ASSERT_OK_AND_ASSIGN(auto context,
  1007. rlwe::RlweContext<TypeParam>::Create(params));
  1008. int substitution_power = 5;
  1009. ASSERT_OK_AND_ASSIGN(auto key, this->SampleKey(context.get()));
  1010. std::vector<typename TypeParam::Int> plaintext1 =
  1011. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  1012. context->GetT());
  1013. std::vector<typename TypeParam::Int> plaintext2 =
  1014. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  1015. context->GetT());
  1016. ASSERT_OK_AND_ASSIGN(auto ciphertext1,
  1017. this->Encrypt(key, plaintext1, context.get()));
  1018. ASSERT_OK_AND_ASSIGN(auto ciphertext2,
  1019. this->Encrypt(key, plaintext2, context.get()));
  1020. ASSERT_OK_AND_ASSIGN(
  1021. auto ciphertext2_sub,
  1022. ciphertext2.Substitute(substitution_power, context->GetNttParams()));
  1023. EXPECT_THAT(ciphertext1 + ciphertext2_sub,
  1024. StatusIs(::absl::StatusCode::kInvalidArgument,
  1025. HasSubstr("must be encrypted with the same key")));
  1026. EXPECT_THAT(ciphertext1 * ciphertext2_sub,
  1027. StatusIs(::absl::StatusCode::kInvalidArgument,
  1028. HasSubstr("must be encrypted with the same key")));
  1029. }
  1030. }
  1031. // Check that the null key has value 0.
  1032. TYPED_TEST(SymmetricRlweEncryptionTest, NullKeyHasValueZero) {
  1033. for (const auto& params :
  1034. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  1035. ASSERT_OK_AND_ASSIGN(auto context,
  1036. rlwe::RlweContext<TypeParam>::Create(params));
  1037. rlwe::Polynomial<TypeParam> zero(context->GetN(),
  1038. context->GetModulusParams());
  1039. ASSERT_OK_AND_ASSIGN(
  1040. auto null_key,
  1041. rlwe::SymmetricRlweKey<TypeParam>::NullKey(
  1042. context->GetLogN(), context->GetVariance(), context->GetLogT(),
  1043. context->GetModulusParams(), context->GetNttParams()));
  1044. EXPECT_THAT(zero, Eq(null_key.Key()));
  1045. }
  1046. }
  1047. // Check the addition and subtraction of keys.
  1048. TYPED_TEST(SymmetricRlweEncryptionTest, AddAndSubKeys) {
  1049. for (const auto& params :
  1050. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  1051. ASSERT_OK_AND_ASSIGN(auto context,
  1052. rlwe::RlweContext<TypeParam>::Create(params));
  1053. ASSERT_OK_AND_ASSIGN(auto key_1, this->SampleKey(context.get()));
  1054. ASSERT_OK_AND_ASSIGN(auto key_2, this->SampleKey(context.get()));
  1055. ASSERT_OK_AND_ASSIGN(auto key_3, key_1.Add(key_2));
  1056. ASSERT_OK_AND_ASSIGN(auto key_4, key_1.Sub(key_2));
  1057. ASSERT_OK_AND_ASSIGN(
  1058. rlwe::Polynomial<TypeParam> poly_3,
  1059. key_1.Key().Add(key_2.Key(), context->GetModulusParams()));
  1060. ASSERT_OK_AND_ASSIGN(
  1061. rlwe::Polynomial<TypeParam> poly_4,
  1062. key_1.Key().Sub(key_2.Key(), context->GetModulusParams()));
  1063. EXPECT_THAT(key_3.Key(), Eq(poly_3));
  1064. EXPECT_THAT(key_4.Key(), Eq(poly_4));
  1065. }
  1066. }
  1067. // Check that decryption works with added and subtracted keys.
  1068. TYPED_TEST(SymmetricRlweEncryptionTest, EncryptAndDecryptWithAddAndSubKeys) {
  1069. for (const auto& params :
  1070. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  1071. ASSERT_OK_AND_ASSIGN(auto context,
  1072. rlwe::RlweContext<TypeParam>::Create(params));
  1073. ASSERT_OK_AND_ASSIGN(auto key_1, this->SampleKey(context.get()));
  1074. ASSERT_OK_AND_ASSIGN(auto key_2, this->SampleKey(context.get()));
  1075. ASSERT_OK_AND_ASSIGN(auto add_keys, key_1.Add(key_2));
  1076. ASSERT_OK_AND_ASSIGN(auto sub_keys, key_1.Sub(key_2));
  1077. std::vector<typename TypeParam::Int> plaintext =
  1078. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  1079. context->GetT());
  1080. ASSERT_OK_AND_ASSIGN(auto add_ciphertext,
  1081. this->Encrypt(add_keys, plaintext, context.get()));
  1082. ASSERT_OK_AND_ASSIGN(auto sub_ciphertext,
  1083. this->Encrypt(sub_keys, plaintext, context.get()));
  1084. ASSERT_OK_AND_ASSIGN(auto decrypted_add_ciphertext,
  1085. rlwe::Decrypt(add_keys, add_ciphertext));
  1086. ASSERT_OK_AND_ASSIGN(auto decrypted_sub_ciphertext,
  1087. rlwe::Decrypt(sub_keys, sub_ciphertext));
  1088. EXPECT_EQ(plaintext, decrypted_add_ciphertext);
  1089. EXPECT_EQ(plaintext, decrypted_sub_ciphertext);
  1090. }
  1091. }
  1092. // Check that the scheme is key homomorphic.
  1093. TYPED_TEST(SymmetricRlweEncryptionTest, IsKeyHomomorphic) {
  1094. for (const auto& params :
  1095. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  1096. ASSERT_OK_AND_ASSIGN(auto context,
  1097. rlwe::RlweContext<TypeParam>::Create(params));
  1098. ASSERT_OK_AND_ASSIGN(auto prng_seed,
  1099. rlwe::SingleThreadPrng::GenerateSeed());
  1100. ASSERT_OK_AND_ASSIGN(auto prng, rlwe::SingleThreadPrng::Create(prng_seed));
  1101. // Generate the keys.
  1102. ASSERT_OK_AND_ASSIGN(auto key_1, this->SampleKey(context.get()));
  1103. ASSERT_OK_AND_ASSIGN(auto key_2, this->SampleKey(context.get()));
  1104. // Generate the plaintexts.
  1105. std::vector<typename TypeParam::Int> plaintext_1 =
  1106. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  1107. context->GetT());
  1108. std::vector<typename TypeParam::Int> plaintext_2 =
  1109. rlwe::testing::SamplePlaintext<TypeParam>(context->GetN(),
  1110. context->GetT());
  1111. ASSERT_OK_AND_ASSIGN(auto plaintext_mont_1,
  1112. rlwe::testing::ConvertToMontgomery<TypeParam>(
  1113. plaintext_1, context->GetModulusParams()));
  1114. ASSERT_OK_AND_ASSIGN(auto plaintext_mont_2,
  1115. rlwe::testing::ConvertToMontgomery<TypeParam>(
  1116. plaintext_2, context->GetModulusParams()));
  1117. auto poly_1 = rlwe::Polynomial<TypeParam>::ConvertToNtt(
  1118. plaintext_mont_1, context->GetNttParams(), context->GetModulusParams());
  1119. auto poly_2 = rlwe::Polynomial<TypeParam>::ConvertToNtt(
  1120. plaintext_mont_2, context->GetNttParams(), context->GetModulusParams());
  1121. // Compute the expected plaintexts.
  1122. std::vector<typename TypeParam::Int> add_plaintext = plaintext_1;
  1123. std::vector<typename TypeParam::Int> sub_plaintext = plaintext_2;
  1124. std::transform(
  1125. plaintext_1.begin(), plaintext_1.end(), plaintext_2.begin(),
  1126. add_plaintext.begin(),
  1127. [&context = context](typename TypeParam::Int u,
  1128. typename TypeParam::Int v) ->
  1129. typename TypeParam::Int { return (u + v) % context->GetT(); });
  1130. std::transform(plaintext_1.begin(), plaintext_1.end(), plaintext_2.begin(),
  1131. sub_plaintext.begin(),
  1132. [&context = context](typename TypeParam::Int u,
  1133. typename TypeParam::Int v) ->
  1134. typename TypeParam::Int {
  1135. return (context->GetT() + u - v) % context->GetT();
  1136. });
  1137. // Sample the "a" to be used in both ciphertexts.
  1138. ASSERT_OK_AND_ASSIGN(auto a,
  1139. rlwe::SamplePolynomialFromPrng<TypeParam>(
  1140. key_1.Len(), prng.get(), key_1.ModulusParams()));
  1141. // Encrypt with the same a and different keys
  1142. ASSERT_OK_AND_ASSIGN(auto poly_ciphertext_1,
  1143. rlwe::internal::Encrypt(key_1, poly_1, a, prng.get()));
  1144. ASSERT_OK_AND_ASSIGN(auto poly_ciphertext_2,
  1145. rlwe::internal::Encrypt(key_2, poly_2, a, prng.get()));
  1146. // Add and Substract the ciphertexts
  1147. ASSERT_OK_AND_ASSIGN(
  1148. auto add_poly_ciphertext,
  1149. poly_ciphertext_1.Add(poly_ciphertext_2, context->GetModulusParams()));
  1150. ASSERT_OK_AND_ASSIGN(
  1151. auto sub_poly_ciphertext,
  1152. poly_ciphertext_1.Sub(poly_ciphertext_2, context->GetModulusParams()));
  1153. // The resulting ciphertexts should be decryptable unded the added (resp.
  1154. // substracted) keys.
  1155. ASSERT_OK_AND_ASSIGN(auto add_keys, key_1.Add(key_2));
  1156. ASSERT_OK_AND_ASSIGN(auto sub_keys, key_1.Sub(key_2));
  1157. ASSERT_OK_AND_ASSIGN(
  1158. auto decrypted_add_ciphertext,
  1159. rlwe::Decrypt(
  1160. add_keys,
  1161. rlwe::SymmetricRlweCiphertext<TypeParam>(
  1162. {add_poly_ciphertext, a.Negate(context->GetModulusParams())}, 1,
  1163. context->GetErrorParams()->B_encryption(),
  1164. context->GetModulusParams(), context->GetErrorParams())));
  1165. ASSERT_OK_AND_ASSIGN(
  1166. auto decrypted_sub_ciphertext,
  1167. rlwe::Decrypt(
  1168. sub_keys,
  1169. rlwe::SymmetricRlweCiphertext<TypeParam>(
  1170. {sub_poly_ciphertext, a.Negate(context->GetModulusParams())}, 1,
  1171. context->GetErrorParams()->B_encryption(),
  1172. context->GetModulusParams(), context->GetErrorParams())));
  1173. EXPECT_EQ(add_plaintext, decrypted_add_ciphertext);
  1174. EXPECT_EQ(sub_plaintext, decrypted_sub_ciphertext);
  1175. }
  1176. }
  1177. // Check that incompatible key cannot be added or subtracted.
  1178. TYPED_TEST(SymmetricRlweEncryptionTest, CannotAddOrSubIncompatibleKeys) {
  1179. for (const auto& params :
  1180. rlwe::testing::ContextParameters<TypeParam>::Value()) {
  1181. ASSERT_OK_AND_ASSIGN(auto context,
  1182. rlwe::RlweContext<TypeParam>::Create(params));
  1183. ASSERT_OK_AND_ASSIGN(auto context_different_variance,
  1184. rlwe::RlweContext<TypeParam>::Create(
  1185. {.modulus = params.modulus,
  1186. .log_n = params.log_n,
  1187. .log_t = params.log_t,
  1188. .variance = params.variance + 1}));
  1189. ASSERT_OK_AND_ASSIGN(
  1190. auto context_different_log_t,
  1191. rlwe::RlweContext<TypeParam>::Create({.modulus = params.modulus,
  1192. .log_n = params.log_n,
  1193. .log_t = params.log_t + 1,
  1194. .variance = params.variance}));
  1195. ASSERT_OK_AND_ASSIGN(auto key_1, this->SampleKey(context.get()));
  1196. ASSERT_OK_AND_ASSIGN(auto key_2,
  1197. this->SampleKey(context_different_variance.get()));
  1198. ASSERT_OK_AND_ASSIGN(auto key_3,
  1199. this->SampleKey(context_different_log_t.get()));
  1200. EXPECT_THAT(
  1201. key_1.Add(key_2),
  1202. StatusIs(::absl::StatusCode::kInvalidArgument,
  1203. HasSubstr("is different than the variance of this key")));
  1204. EXPECT_THAT(
  1205. key_1.Sub(key_2),
  1206. StatusIs(::absl::StatusCode::kInvalidArgument,
  1207. HasSubstr("is different than the variance of this key")));
  1208. EXPECT_THAT(key_1.Add(key_3),
  1209. StatusIs(::absl::StatusCode::kInvalidArgument,
  1210. HasSubstr("is different than the log_t of this key")));
  1211. EXPECT_THAT(key_1.Sub(key_3),
  1212. StatusIs(::absl::StatusCode::kInvalidArgument,
  1213. HasSubstr("is different than the log_t of this key")));
  1214. }
  1215. }
  1216. } // namespace