1use std::borrow::{Borrow, Cow};
4
5use rayon::join;
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7
8use crate::traits::*;
9use crate::{
10 BigInt, DecryptionKey, EncryptionKey, Keypair, MinimalDecryptionKey, MinimalEncryptionKey,
11 Paillier, RawCiphertext, RawPlaintext,
12};
13use rust_bigint::traits::*;
14#[cfg(feature = "num_bigint")]
15use num_traits::One;
16
17impl Keypair {
18 pub fn keys(&self) -> (EncryptionKey, DecryptionKey) {
20 (EncryptionKey::from(self), DecryptionKey::from(self))
21 }
22}
23
24impl<'p, 'q> From<(&'p BigInt, &'q BigInt)> for Keypair {
25 fn from((p, q): (&'p BigInt, &'q BigInt)) -> Keypair {
26 Keypair {
27 p: p.clone(),
28 q: q.clone(),
29 }
30 }
31}
32
33impl<'kp> From<&'kp Keypair> for MinimalEncryptionKey {
34 fn from(keypair: &'kp Keypair) -> Self {
35 MinimalEncryptionKey {
36 n: &keypair.p * &keypair.q,
37 }
38 }
39}
40
41impl<'e> From<&'e EncryptionKey> for MinimalEncryptionKey {
42 fn from(ek: &'e EncryptionKey) -> Self {
43 MinimalEncryptionKey { n: ek.n.clone() }
44 }
45}
46
47impl<'e> From<MinimalEncryptionKey> for EncryptionKey {
48 fn from(ek: MinimalEncryptionKey) -> Self {
49 let nn = &ek.n * &ek.n;
50 let n = ek.n;
51 EncryptionKey { n, nn }
52 }
53}
54
55impl<'kp> From<&'kp Keypair> for EncryptionKey {
56 fn from(keypair: &'kp Keypair) -> Self {
57 let minimal = MinimalEncryptionKey::from(keypair);
58 EncryptionKey::from(minimal)
59 }
60}
61
62impl<'n> From<&'n BigInt> for EncryptionKey {
64 fn from(n: &'n BigInt) -> Self {
65 let minimal = MinimalEncryptionKey { n: n.clone() };
66 EncryptionKey::from(minimal)
67 }
68}
69
70impl Serialize for EncryptionKey {
71 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
72 let minimal = MinimalEncryptionKey::from(self);
73 minimal.serialize(serializer)
74 }
75}
76
77impl<'de> Deserialize<'de> for EncryptionKey {
78 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
79 let minimal = MinimalEncryptionKey::deserialize(deserializer)?;
80 Ok(EncryptionKey::from(minimal))
81 }
82}
83
84impl<'kp> From<&'kp Keypair> for MinimalDecryptionKey {
85 fn from(keypair: &'kp Keypair) -> Self {
86 MinimalDecryptionKey {
87 p: keypair.p.clone(),
88 q: keypair.q.clone(),
89 }
90 }
91}
92
93impl<'e> From<&'e DecryptionKey> for MinimalDecryptionKey {
94 fn from(dk: &'e DecryptionKey) -> Self {
95 MinimalDecryptionKey {
96 p: dk.p.clone(),
97 q: dk.q.clone(),
98 }
99 }
100}
101
102impl<'e> From<MinimalDecryptionKey> for DecryptionKey {
103 fn from(dk: MinimalDecryptionKey) -> Self {
104 let p = dk.p;
105 let q = dk.q;
106
107 DecryptionKey { p, q }
108 }
109}
110
111impl<'kp> From<&'kp Keypair> for DecryptionKey {
112 fn from(keypair: &'kp Keypair) -> DecryptionKey {
113 let minimal = MinimalDecryptionKey::from(keypair);
114 DecryptionKey::from(minimal)
115 }
116}
117
118impl Serialize for DecryptionKey {
119 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
120 let minimal = MinimalDecryptionKey::from(self);
121 minimal.serialize(serializer)
122 }
123}
124
125impl<'de> Deserialize<'de> for DecryptionKey {
126 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
127 let minimal = MinimalDecryptionKey::deserialize(deserializer)?;
128 Ok(DecryptionKey::from(minimal))
129 }
130}
131
132#[derive(Debug, PartialEq)]
133pub struct Randomness(pub BigInt);
134
135#[derive(Debug, PartialEq)]
136pub struct PrecomputedRandomness(pub BigInt);
137
138impl Randomness {
139 pub fn sample(ek: &EncryptionKey) -> Randomness {
140 Randomness(BigInt::sample_below(&ek.n))
141 }
142}
143
144impl From<BigInt> for Randomness {
145 fn from(x: BigInt) -> Randomness {
146 Randomness(x)
147 }
148}
149
150impl<'b> From<&'b BigInt> for Randomness {
151 fn from(x: &'b BigInt) -> Randomness {
152 Randomness(x.clone())
153 }
154}
155
156impl<'b> From<BigInt> for RawPlaintext<'b> {
157 fn from(x: BigInt) -> Self {
158 RawPlaintext(Cow::Owned(x))
159 }
160}
161
162impl<'b> From<&'b BigInt> for RawPlaintext<'b> {
163 fn from(x: &'b BigInt) -> Self {
164 RawPlaintext(Cow::Borrowed(x))
165 }
166}
167
168impl<'b> From<RawPlaintext<'b>> for BigInt {
169 fn from(x: RawPlaintext<'b>) -> Self {
170 x.0.into_owned()
171 }
172}
173
174impl<'b> From<BigInt> for RawCiphertext<'b> {
175 fn from(x: BigInt) -> Self {
176 RawCiphertext(Cow::Owned(x))
177 }
178}
179
180impl<'b> From<&'b BigInt> for RawCiphertext<'b> {
181 fn from(x: &'b BigInt) -> Self {
182 RawCiphertext(Cow::Borrowed(x))
183 }
184}
185
186impl<'b> From<RawCiphertext<'b>> for BigInt {
187 fn from(x: RawCiphertext<'b>) -> Self {
188 x.0.into_owned()
189 }
190}
191
192impl<'m, 'd> Encrypt<EncryptionKey, RawPlaintext<'m>, RawCiphertext<'d>> for Paillier {
193 fn encrypt(ek: &EncryptionKey, m: RawPlaintext<'m>) -> RawCiphertext<'d> {
194 let r = Randomness::sample(&ek);
195 let rn = BigInt::mod_pow(&r.0, &ek.n, &ek.nn);
196 let gm: BigInt = (m.0.borrow() as &BigInt * &ek.n + 1) % &ek.nn;
197 let c = (gm * rn) % &ek.nn;
198 RawCiphertext(Cow::Owned(c))
199 }
200}
201
202impl<'m, 'r, 'd>
203 EncryptWithChosenRandomness<EncryptionKey, RawPlaintext<'m>, &'r Randomness, RawCiphertext<'d>>
204 for Paillier
205{
206 fn encrypt_with_chosen_randomness(
207 ek: &EncryptionKey,
208 m: RawPlaintext<'m>,
209 r: &'r Randomness,
210 ) -> RawCiphertext<'d> {
211 let rn = BigInt::mod_pow(&r.0, &ek.n, &ek.nn);
212 let gm: BigInt = (m.0.borrow() as &BigInt * &ek.n + 1) % &ek.nn;
213 let c = (gm * rn) % &ek.nn;
214 RawCiphertext(Cow::Owned(c))
215 }
216}
217
218impl<'m, 'r, 'd>
219 EncryptWithChosenRandomness<
220 EncryptionKey,
221 RawPlaintext<'m>,
222 &'r PrecomputedRandomness,
223 RawCiphertext<'d>,
224 > for Paillier
225{
226 fn encrypt_with_chosen_randomness(
227 ek: &EncryptionKey,
228 m: RawPlaintext<'m>,
229 rn: &'r PrecomputedRandomness,
230 ) -> RawCiphertext<'d> {
231 let gm: BigInt = (m.0.borrow() as &BigInt * &ek.n + 1) % &ek.nn;
232 let c = (gm * &rn.0) % &ek.nn;
233 RawCiphertext(Cow::Owned(c))
234 }
235}
236
237impl<'m, 'd> Encrypt<DecryptionKey, RawPlaintext<'m>, RawCiphertext<'d>> for Paillier {
238 fn encrypt(dk: &DecryptionKey, m: RawPlaintext<'m>) -> RawCiphertext<'d> {
239 let dk_pp = &dk.p * &dk.p;
240 let dk_qq = &dk.q * &dk.q;
241 let dk_n = &dk.q * &dk.p;
242 let dk_ppinv = BigInt::mod_inv(&dk_pp, &dk_qq);
243 let (mp, mq) = crt_decompose(m.0.borrow(), &dk_pp, &dk_qq);
244 let (cp, cq) = join(
245 || {
246 let rp = BigInt::sample_below(&dk.p);
247 let rnp = BigInt::mod_pow(&rp, &dk_n, &dk_pp);
248 let gmp = (1 + mp * &dk_n) % &dk_pp; (gmp * rnp) % &dk_pp
250 },
251 || {
252 let rq = BigInt::sample_below(&dk.q);
253 let rnq = BigInt::mod_pow(&rq, &dk_n, &dk_qq);
254 let gmq = (1 + mq * &dk_n) % &dk_qq; (gmq * rnq) % &dk_qq
256 },
257 );
258 let c = crt_recombine(cp, cq, &dk_pp, &dk_qq, &dk_ppinv);
259 RawCiphertext(Cow::Owned(c))
260 }
261}
262
263impl<'m, 'r, 'd>
264 EncryptWithChosenRandomness<DecryptionKey, RawPlaintext<'m>, &'r Randomness, RawCiphertext<'d>>
265 for Paillier
266{
267 fn encrypt_with_chosen_randomness(
268 dk: &DecryptionKey,
269 m: RawPlaintext<'m>,
270 r: &'r Randomness,
271 ) -> RawCiphertext<'d> {
272 let dk_pp = &dk.p * &dk.p;
273 let dk_qq = &dk.q * &dk.q;
274 let dk_n = &dk.q * &dk.p;
275 let dk_ppinv = BigInt::mod_inv(&dk_pp, &dk_qq);
276 let (mp, mq) = crt_decompose(m.0.borrow(), &dk_pp, &dk_qq);
277 let (rp, rq) = crt_decompose(&r.0, &dk_pp, &dk_qq);
278 let (cp, cq) = join(
279 || {
280 let rnp = BigInt::mod_pow(&rp, &dk_n, &dk_pp);
281 let gmp = (1 + mp * &dk_n) % &dk_pp; (gmp * rnp) % &dk_pp
283 },
284 || {
285 let rnq = BigInt::mod_pow(&rq, &dk_n, &dk_qq);
286 let gmq = (1 + mq * &dk_n) % &dk_qq; (gmq * rnq) % &dk_qq
288 },
289 );
290 let c = crt_recombine(cp, cq, &dk_pp, &dk_qq, &dk_ppinv);
291 RawCiphertext(Cow::Owned(c))
292 }
293}
294
295impl<'m, 'r, 'd>
296 EncryptWithChosenRandomness<
297 DecryptionKey,
298 RawPlaintext<'m>,
299 &'r PrecomputedRandomness,
300 RawCiphertext<'d>,
301 > for Paillier
302{
303 fn encrypt_with_chosen_randomness(
304 dk: &DecryptionKey,
305 m: RawPlaintext<'m>,
306 rn: &'r PrecomputedRandomness,
307 ) -> RawCiphertext<'d> {
308 let dk_n = &dk.q * &dk.p;
309 let dk_nn = &dk_n * &dk_n;
310 let gm = (1 + m.0.borrow() as &BigInt * &dk_n) % &dk_nn;
311 let c = (gm * &rn.0) % &dk_nn;
312 RawCiphertext(Cow::Owned(c))
313 }
314}
315
316impl<'ek, 'r> PrecomputeRandomness<&'ek EncryptionKey, &'r BigInt, PrecomputedRandomness>
317 for Paillier
318{
319 fn precompute(ek: &'ek EncryptionKey, r: &'r BigInt) -> PrecomputedRandomness {
320 let rn = BigInt::mod_pow(r, &ek.n, &ek.nn);
321 PrecomputedRandomness(rn)
322 }
323}
324
325impl<'c, 'd> Rerandomize<EncryptionKey, RawCiphertext<'c>, RawCiphertext<'d>> for Paillier {
326 fn rerandomize(ek: &EncryptionKey, c: RawCiphertext<'c>) -> RawCiphertext<'d> {
327 let r = BigInt::sample_below(&ek.n);
328 let rn = BigInt::mod_pow(&r, &ek.n, &ek.nn);
329 let d = (c.0.borrow() as &BigInt * rn) % &ek.nn;
330 RawCiphertext(Cow::Owned(d))
331 }
332}
333
334impl<'c, 'm> Decrypt<DecryptionKey, RawCiphertext<'c>, RawPlaintext<'m>> for Paillier {
338 fn decrypt(dk: &DecryptionKey, c: RawCiphertext<'c>) -> RawPlaintext<'m> {
339 Self::decrypt(dk, &c)
340 }
341}
342
343impl<'c, 'm> Decrypt<DecryptionKey, &'c RawCiphertext<'c>, RawPlaintext<'m>> for Paillier {
347 fn decrypt(dk: &DecryptionKey, c: &'c RawCiphertext<'c>) -> RawPlaintext<'m> {
348 let dk_qq = &dk.q * &dk.q;
349 let dk_pp = &dk.p * &dk.p;
350 let dk_n = &dk.p * &dk.q;
351 let dk_pinv = BigInt::mod_inv(&dk.p, &dk.q);
352 let dk_qminusone = &dk.q - BigInt::one();
353 let dk_pminusone = &dk.p - BigInt::one();
354 let dk_hp = h(&dk.p, &dk_pp, &dk_n);
355 let dk_hq = h(&dk.q, &dk_qq, &dk_n);
356 let (cp, cq) = crt_decompose(c.0.borrow(), &dk_pp, &dk_qq);
357 let (mp, mq) = join(
359 || {
360 let dp = BigInt::mod_pow(&cp, &dk_pminusone, &dk_pp);
362 let lp = l(&dp, &dk.p);
363 (&lp * &dk_hp) % &dk.p
364 },
365 || {
366 let dq = BigInt::mod_pow(&cq, &dk_qminusone, &dk_qq);
368 let lq = l(&dq, &dk.q);
369 (&lq * &dk_hq) % &dk.q
370 },
371 );
372 let m = crt_recombine(mp, mq, &dk.p, &dk.q, &dk_pinv);
374 RawPlaintext(Cow::Owned(m))
375 }
376}
377
378impl<'c, 'm> Open<DecryptionKey, RawCiphertext<'c>, RawPlaintext<'m>, Randomness> for Paillier {
379 fn open(dk: &DecryptionKey, c: RawCiphertext<'c>) -> (RawPlaintext<'m>, Randomness) {
380 Self::open(dk, &c)
381 }
382}
383
384impl<'c, 'm> Open<DecryptionKey, &'c RawCiphertext<'c>, RawPlaintext<'m>, Randomness> for Paillier {
385 fn open(dk: &DecryptionKey, c: &'c RawCiphertext<'c>) -> (RawPlaintext<'m>, Randomness) {
386 let dk_n = &dk.p * &dk.q;
387 let dk_nn = &dk_n * &dk_n;
388
389 let m = Self::decrypt(dk, c);
390 let gminv = (BigInt::one() - (m.0.borrow() as &BigInt) * &dk_n) % &dk_nn;
391 let rn = (c.0.borrow() as &BigInt * gminv) % &dk_nn;
392 let r = extract_nroot(dk, &rn);
393 (m, Randomness(r))
394 }
395}
396
397impl<'c1, 'c2, 'd> Add<EncryptionKey, RawCiphertext<'c1>, RawCiphertext<'c2>, RawCiphertext<'d>>
398 for Paillier
399{
400 fn add(
401 ek: &EncryptionKey,
402 c1: RawCiphertext<'c1>,
403 c2: RawCiphertext<'c2>,
404 ) -> RawCiphertext<'d> {
405 let d = (c1.0.borrow() as &BigInt * c2.0.borrow() as &BigInt) % &ek.nn;
406 RawCiphertext(Cow::Owned(d))
407 }
408}
409
410impl<'c, 'm, 'd> Add<EncryptionKey, RawCiphertext<'c>, RawPlaintext<'m>, RawCiphertext<'d>>
411 for Paillier
412{
413 fn add(ek: &EncryptionKey, c: RawCiphertext<'c>, m: RawPlaintext<'m>) -> RawCiphertext<'d> {
414 let c1 = c.0.borrow() as &BigInt;
415 let c2 = (m.0.borrow() as &BigInt * &ek.n + 1) % &ek.nn;
416 let d = (c1 * c2) % &ek.nn;
417 RawCiphertext(Cow::Owned(d))
418 }
419}
420
421impl<'c, 'm, 'd> Add<EncryptionKey, RawPlaintext<'m>, RawCiphertext<'c>, RawCiphertext<'d>>
422 for Paillier
423{
424 fn add(ek: &EncryptionKey, m: RawPlaintext<'m>, c: RawCiphertext<'c>) -> RawCiphertext<'d> {
425 let c1 = (m.0.borrow() as &BigInt * &ek.n + 1) % &ek.nn;
426 let c2 = c.0.borrow() as &BigInt;
427 let d = (c1 * c2) % &ek.nn;
428 RawCiphertext(Cow::Owned(d))
429 }
430}
431
432impl<'c, 'm, 'd> Mul<EncryptionKey, RawCiphertext<'c>, RawPlaintext<'m>, RawCiphertext<'d>>
433 for Paillier
434{
435 fn mul(ek: &EncryptionKey, c: RawCiphertext<'c>, m: RawPlaintext<'m>) -> RawCiphertext<'d> {
436 RawCiphertext(Cow::Owned(BigInt::mod_pow(
437 c.0.borrow(),
438 m.0.borrow(),
439 &ek.nn,
440 )))
441 }
442}
443
444impl<'c, 'm, 'd> Mul<EncryptionKey, RawPlaintext<'m>, RawCiphertext<'c>, RawCiphertext<'d>>
445 for Paillier
446{
447 fn mul(ek: &EncryptionKey, m: RawPlaintext<'m>, c: RawCiphertext<'c>) -> RawCiphertext<'d> {
448 RawCiphertext(Cow::Owned(BigInt::mod_pow(
449 c.0.borrow(),
450 m.0.borrow(),
451 &ek.nn,
452 )))
453 }
454}
455
456fn h(p: &BigInt, pp: &BigInt, n: &BigInt) -> BigInt {
457 let gp = (1 - n) % pp;
464 let lp = l(&gp, p);
466 BigInt::mod_inv(&lp, p)
468}
469
470fn l(u: &BigInt, n: &BigInt) -> BigInt {
471 (u - 1) / n
472}
473
474fn crt_decompose<X, M1, M2>(x: X, m1: M1, m2: M2) -> (BigInt, BigInt)
475where
476 X: Borrow<BigInt>,
477 M1: Borrow<BigInt>,
478 M2: Borrow<BigInt>,
479{
480 (x.borrow() % m1.borrow(), x.borrow() % m2.borrow())
481}
482
483fn crt_recombine<X1, X2, M1, M2, I>(x1: X1, x2: X2, m1: M1, m2: M2, m1inv: I) -> BigInt
484where
485 X1: Borrow<BigInt>,
486 X2: Borrow<BigInt>,
487 M1: Borrow<BigInt>,
488 M2: Borrow<BigInt>,
489 I: Borrow<BigInt>,
490{
491 let diff = BigInt::mod_sub(x2.borrow(), x1.borrow(), m2.borrow());
492 let u = (diff * m1inv.borrow()) % m2.borrow();
497 x1.borrow() + (u * m1.borrow())
498}
499
500pub fn extract_nroot(dk: &DecryptionKey, z: &BigInt) -> BigInt {
502 let dk_n = &dk.p * &dk.q;
503
504 let dk_pinv = BigInt::mod_inv(&dk.p, &dk.q);
505 let dk_qminusone = &dk.q - BigInt::one();
506 let dk_pminusone = &dk.p - BigInt::one();
507
508 let dk_phi = &dk_pminusone * &dk_qminusone;
509 let dk_dn = BigInt::mod_inv(&dk_n, &dk_phi);
510 let (dk_dp, dk_dq) = crt_decompose(dk_dn, &dk_pminusone, &dk_qminusone);
511 let (zp, zq) = crt_decompose(z, &dk.p, &dk.q);
512
513 let rp = BigInt::mod_pow(&zp, &dk_dp, &dk.p);
514 let rq = BigInt::mod_pow(&zq, &dk_dq, &dk.q);
515
516 crt_recombine(rp, rq, &dk.p, &dk.q, &dk_pinv)
517}
518
519#[cfg(test)]
520mod tests {
521
522 use super::*;
523
524 extern crate serde_json;
525
526 fn test_keypair() -> Keypair {
527 let p = str::parse("148677972634832330983979593310074301486537017973460461278300587514468301043894574906886127642530475786889672304776052879927627556769456140664043088700743909632312483413393134504352834240399191134336344285483935856491230340093391784574980688823380828143810804684752914935441384845195613674104960646037368551517").unwrap();
528 let q = str::parse("158741574437007245654463598139927898730476924736461654463975966787719309357536545869203069369466212089132653564188443272208127277664424448947476335413293018778018615899291704693105620242763173357203898195318179150836424196645745308205164116144020613415407736216097185962171301808761138424668335445923774195463").unwrap();
529 Keypair { p, q }
530 }
531
532 #[test]
533 fn test_correct_encryption_decryption() {
534 let (ek, dk) = test_keypair().keys();
535
536 let p = RawPlaintext::from(BigInt::from(10));
537 let c = Paillier::encrypt(&ek, p.clone());
538
539 let recovered_p = Paillier::decrypt(&dk, c);
540 assert_eq!(recovered_p, p);
541 }
542
543 #[test]
544 fn test_correct_opening() {
545 let (ek, dk) = test_keypair().keys();
546
547 let c = Paillier::encrypt(&ek, RawPlaintext::from(BigInt::from(10)));
548 let (m, r) = Paillier::open(&dk, &c);
549 let d = Paillier::encrypt_with_chosen_randomness(&ek, m, &r);
550 assert_eq!(c, d);
551 }
552
553 #[test]
554 fn test_correct_addition() {
555 let (ek, dk) = test_keypair().keys();
556
557 let m1 = RawPlaintext::from(BigInt::from(10));
558 let c1 = Paillier::encrypt(&ek, m1);
559 let m2 = RawPlaintext::from(BigInt::from(20));
560 let c2 = Paillier::encrypt(&ek, m2);
561
562 let c = Paillier::add(&ek, c1, c2);
563 let m = Paillier::decrypt(&dk, c);
564 assert_eq!(m, BigInt::from(30).into());
565 }
566
567 #[test]
568 fn correct_multiplication() {
569 let (ek, dk) = test_keypair().keys();
570
571 let m1 = RawPlaintext::from(BigInt::from(10));
572 let c1 = Paillier::encrypt(&ek, m1);
573 let m2 = RawPlaintext::from(BigInt::from(20));
574
575 let c = Paillier::mul(&ek, c1, m2);
576 let m = Paillier::decrypt(&dk, c);
577 assert_eq!(m, BigInt::from(200).into());
578 }
579
580 #[cfg(feature = "keygen")]
581 #[test]
582 fn test_correct_keygen() {
583 let (ek, dk): (EncryptionKey, _) = Paillier::keypair_with_modulus_size(2048).keys();
584
585 let m = RawPlaintext::from(BigInt::from(10));
586 let c = Paillier::encrypt(&ek, m.clone()); let recovered_m = Paillier::decrypt(&dk, c);
589 assert_eq!(recovered_m, m);
590 }
591
592 #[test]
593 fn test_key_serialization() {
594 let (ek, dk) = test_keypair().keys();
595
596 let ek_serialized = serde_json::to_string(&ek).unwrap();
597 let ek_recovered: EncryptionKey = serde_json::from_str(&ek_serialized).unwrap();
598 assert_eq!(ek, ek_recovered);
599
600 let dk_serialized = serde_json::to_string(&dk).unwrap();
601 let dk_recovered: DecryptionKey = serde_json::from_str(&dk_serialized).unwrap();
602 assert_eq!(dk, dk_recovered);
603 }
604
605 #[test]
606 fn test_failing_deserialize() {
607 let illformatted = "{\"n\":\"12345abcdefg\"}";
608
609 let result: Result<EncryptionKey, _> = serde_json::from_str(&illformatted);
610 assert!(result.is_err())
611 }
612}