1use anyhow::{format_err, Error};
9use bssl_sys::{
10 BN_CTX_free, BN_CTX_new, BN_add, BN_asc2bn, BN_bin2bn, BN_bn2bin, BN_bn2dec, BN_cmp, BN_copy,
11 BN_equal_consttime, BN_free, BN_is_odd, BN_is_one, BN_is_zero, BN_mod_add, BN_mod_exp,
12 BN_mod_inverse, BN_mod_mul, BN_mod_sqr, BN_mod_sqrt, BN_new, BN_nnmod, BN_num_bits,
13 BN_num_bytes, BN_one, BN_rand_range, BN_rshift1, BN_set_negative, BN_set_u64, BN_sub, BN_zero,
14 EC_GROUP_free, EC_GROUP_get_curve_GFp, EC_GROUP_get_order, EC_GROUP_new_by_curve_name,
15 EC_POINT_add, EC_POINT_free, EC_POINT_get_affine_coordinates_GFp, EC_POINT_invert,
16 EC_POINT_is_at_infinity, EC_POINT_mul, EC_POINT_new, EC_POINT_set_affine_coordinates_GFp,
17 ERR_get_error, ERR_reason_error_string, NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1,
18 OPENSSL_free, BIGNUM, BN_CTX, EC_GROUP, EC_POINT,
19};
20use num_derive::{FromPrimitive, ToPrimitive};
21use std::cmp::Ordering;
22use std::convert::TryInto;
23use std::ffi::CString;
24use std::fmt;
25use std::ptr::NonNull;
26
27fn ptr_or_error<T>(ptr: *mut T) -> Result<NonNull<T>, Error> {
28 match NonNull::new(ptr) {
29 Some(non_null) => Ok(non_null),
30 None => return Err(format_err!("Found null pointer from BoringSSL")),
31 }
32}
33
34fn one_or_error(res: std::os::raw::c_int) -> Result<(), Error> {
35 match res {
36 1 => Ok(()),
37 _ => unsafe {
38 let error_code = ERR_get_error();
39 let error_reason_ptr = ERR_reason_error_string(error_code);
40 if error_reason_ptr.is_null() {
41 return Err(format_err!("BoringSSL failed to perform an operation."));
42 }
43 let error_reason = std::ffi::CStr::from_ptr(error_reason_ptr).to_string_lossy();
44 return Err(format_err!("BoringSSL failed to perform an operation: {}", error_reason));
45 },
47 }
48}
49
50pub struct Bignum(NonNull<BIGNUM>);
52
53impl Drop for Bignum {
54 fn drop(&mut self) {
55 unsafe { BN_free(self.0.as_mut()) }
56 }
57}
58
59pub struct BignumCtx(NonNull<BN_CTX>);
61
62impl Drop for BignumCtx {
63 fn drop(&mut self) {
64 unsafe { BN_CTX_free(self.0.as_mut()) }
65 }
66}
67
68impl BignumCtx {
69 pub fn new() -> Result<Self, Error> {
70 ptr_or_error(unsafe { BN_CTX_new() }).map(Self)
71 }
72}
73
74impl Bignum {
75 pub fn new() -> Result<Self, Error> {
76 ptr_or_error(unsafe { BN_new() }).map(Self)
77 }
78
79 #[allow(dead_code)]
82 pub fn zero() -> Result<Self, Error> {
83 let result = Self::new()?;
84 unsafe {
85 BN_zero(result.0.as_ptr());
86 }
87 Ok(result)
88 }
89
90 pub fn one() -> Result<Self, Error> {
92 let result = Self::new()?;
93 one_or_error(unsafe { BN_one(result.0.as_ptr()) })?;
94 Ok(result)
95 }
96
97 pub fn rand(max: &Bignum) -> Result<Self, Error> {
99 let result = Self::new()?;
100 one_or_error(unsafe { BN_rand_range(result.0.as_ptr(), max.0.as_ptr()) })?;
101 Ok(result)
102 }
103
104 pub fn new_from_slice(bytes: &[u8]) -> Result<Self, Error> {
107 if bytes.is_empty() {
108 Self::new_from_u64(0)
109 } else {
110 let bytes_len = bytes.len().try_into().unwrap();
112 ptr_or_error(unsafe {
113 BN_bin2bn(&bytes[0] as *const u8, bytes_len, std::ptr::null_mut())
114 })
115 .map(Self)
116 }
117 }
118
119 #[allow(dead_code)]
123 pub fn new_from_string(ascii: &str) -> Result<Self, Error> {
124 let mut bignum = std::ptr::null_mut();
125 let ascii = CString::new(ascii)?;
126 one_or_error(unsafe { BN_asc2bn(&mut bignum as *mut *mut BIGNUM, ascii.as_ptr()) })?;
127 ptr_or_error(bignum).map(Bignum)
128 }
129
130 pub fn new_from_u64(value: u64) -> Result<Self, Error> {
131 let mut bignum = Self::new()?;
132 one_or_error(unsafe { BN_set_u64(bignum.0.as_mut(), value) })?;
133 Ok(bignum)
134 }
135
136 pub fn set_negative(self) -> Self {
138 unsafe { BN_set_negative(self.0.as_ptr(), 1) };
139 self
140 }
141
142 pub fn copy(&self) -> Result<Self, Error> {
143 let mut copy = Self::new()?;
144 ptr_or_error(unsafe { BN_copy(copy.0.as_mut(), self.0.as_ptr()) })?;
145 Ok(copy)
146 }
147
148 pub fn add(&self, mut b: Self) -> Result<Self, Error> {
151 one_or_error(unsafe { BN_add(b.0.as_mut(), self.0.as_ptr(), b.0.as_ptr()) })?;
152 Ok(b)
153 }
154
155 pub fn sub(&self, mut b: Self) -> Result<Self, Error> {
158 one_or_error(unsafe { BN_sub(b.0.as_mut(), self.0.as_ptr(), b.0.as_ptr()) })?;
159 Ok(b)
160 }
161
162 pub fn mod_nonnegative(&self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
164 let mut result = Self::new()?;
165 one_or_error(unsafe {
166 BN_nnmod(result.0.as_mut(), self.0.as_ptr(), m.0.as_ptr(), ctx.0.as_ptr())
167 })?;
168 Ok(result)
169 }
170
171 pub fn mod_add(&self, b: &Self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
173 let mut result = Self::new()?;
174 one_or_error(unsafe {
175 BN_mod_add(
176 result.0.as_mut(),
177 self.0.as_ptr(),
178 b.0.as_ptr(),
179 m.0.as_ptr(),
180 ctx.0.as_ptr(),
181 )
182 })?;
183 Ok(result)
184 }
185
186 pub fn mod_mul(&self, b: &Self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
188 let mut result = Self::new()?;
189 one_or_error(unsafe {
190 BN_mod_mul(
191 result.0.as_mut(),
192 self.0.as_ptr(),
193 b.0.as_ptr(),
194 m.0.as_ptr(),
195 ctx.0.as_ptr(),
196 )
197 })?;
198 Ok(result)
199 }
200
201 pub fn mod_inverse(&self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
203 let mut result = Self::new()?;
204 ptr_or_error(unsafe {
205 BN_mod_inverse(result.0.as_mut(), self.0.as_ptr(), m.0.as_ptr(), ctx.0.as_ptr())
206 })?;
207 Ok(result)
208 }
209
210 pub fn mod_exp(&self, p: &Self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
212 let mut result = Self::new()?;
213 one_or_error(unsafe {
214 BN_mod_exp(
215 result.0.as_mut(),
216 self.0.as_ptr(),
217 p.0.as_ptr(),
218 m.0.as_ptr(),
219 ctx.0.as_ptr(),
220 )
221 })?;
222 Ok(result)
223 }
224
225 pub fn mod_square(&self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
227 let mut result = Self::new()?;
228 one_or_error(unsafe {
229 BN_mod_sqr(result.0.as_mut(), self.0.as_ptr(), m.0.as_ptr(), ctx.0.as_ptr())
230 })?;
231 Ok(result)
232 }
233
234 pub fn mod_sqrt(&self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
237 let mut result = Self::new()?;
238 ptr_or_error(unsafe {
239 BN_mod_sqrt(result.0.as_mut(), self.0.as_ptr(), m.0.as_ptr(), ctx.0.as_ptr())
240 })?;
241 Ok(result)
242 }
243
244 pub fn rshift1(&self) -> Result<Self, Error> {
246 let mut result = Self::new()?;
247 one_or_error(unsafe { BN_rshift1(result.0.as_mut(), self.0.as_ptr()) })?;
248 Ok(result)
249 }
250
251 pub fn is_one(&self) -> bool {
252 unsafe { BN_is_one(self.0.as_ptr()) == 1 }
253 }
254
255 pub fn is_zero(&self) -> bool {
256 unsafe { BN_is_zero(self.0.as_ptr()) == 1 }
257 }
258
259 pub fn is_odd(&self) -> bool {
260 unsafe { BN_is_odd(self.0.as_ptr()) == 1 }
261 }
262
263 pub fn len(&self) -> usize {
265 unsafe { BN_num_bytes(self.0.as_ptr()) as usize }
266 }
267
268 pub fn bits(&self) -> usize {
271 unsafe { BN_num_bits(self.0.as_ptr()) as usize }
272 }
273
274 pub fn to_be_vec(&self, min_length: usize) -> Vec<u8> {
277 let len = self.len();
278 let padded_len = std::cmp::max(len, min_length);
279 let mut out = vec![0; padded_len];
280 if len != 0 {
281 unsafe {
282 BN_bn2bin(self.0.as_ptr(), &mut out[padded_len - len] as *mut u8);
283 }
284 }
285 out
286 }
287}
288
289impl PartialEq for Bignum {
290 fn eq(&self, other: &Self) -> bool {
291 unsafe { BN_equal_consttime(self.0.as_ptr(), other.0.as_ptr()) == 1 }
292 }
293}
294impl Eq for Bignum {}
295
296impl std::cmp::Ord for Bignum {
297 fn cmp(&self, other: &Self) -> Ordering {
298 unsafe { BN_cmp(self.0.as_ptr(), other.0.as_ptr()) }.cmp(&0)
299 }
300}
301
302impl PartialOrd for Bignum {
303 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
304 Some(self.cmp(other))
305 }
306}
307
308impl fmt::Display for Bignum {
309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310 unsafe {
311 let ptr = BN_bn2dec(self.0.as_ptr());
312 let res = std::ffi::CStr::from_ptr(ptr).to_string_lossy().fmt(f);
314 OPENSSL_free(ptr as *mut ::std::os::raw::c_void);
315 res
316 }
317 }
318}
319
320impl fmt::Debug for Bignum {
321 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322 write!(f, "Bignum({})", self)
323 }
324}
325
326#[derive(Clone, Copy, Debug, FromPrimitive, ToPrimitive)]
328pub enum EcGroupId {
329 P256 = 19,
330 P384 = 20,
331 P521 = 21,
332}
333
334impl EcGroupId {
335 fn nid(&self) -> i32 {
336 match self {
337 EcGroupId::P256 => NID_X9_62_prime256v1,
338 EcGroupId::P384 => NID_secp384r1,
339 EcGroupId::P521 => NID_secp521r1,
340 }
341 }
342}
343
344pub struct EcGroup(NonNull<EC_GROUP>);
347
348pub struct EcGroupParams {
349 pub p: Bignum,
351
352 pub a: Bignum,
354 pub b: Bignum,
355}
356
357impl Drop for EcGroup {
358 fn drop(&mut self) {
359 unsafe { EC_GROUP_free(self.0.as_mut()) }
360 }
361}
362
363impl EcGroup {
364 pub fn new(id: EcGroupId) -> Result<Self, Error> {
365 ptr_or_error(unsafe { EC_GROUP_new_by_curve_name(id.nid()) }).map(Self)
366 }
367
368 pub fn get_params(&self, ctx: &BignumCtx) -> Result<EcGroupParams, Error> {
370 let p = Bignum::new()?;
371 let a = Bignum::new()?;
372 let b = Bignum::new()?;
373
374 one_or_error(unsafe {
375 EC_GROUP_get_curve_GFp(
376 self.0.as_ptr(),
377 p.0.as_ptr(),
378 a.0.as_ptr(),
379 b.0.as_ptr(),
380 ctx.0.as_ptr(),
381 )
382 })?;
383 Ok(EcGroupParams { p, a, b })
384 }
385
386 pub fn get_order(&self, ctx: &BignumCtx) -> Result<Bignum, Error> {
388 let order = Bignum::new()?;
389 one_or_error(unsafe {
390 EC_GROUP_get_order(self.0.as_ptr(), order.0.as_ptr(), ctx.0.as_ptr())
391 })?;
392 Ok(order)
393 }
394}
395
396pub struct EcPoint(NonNull<EC_POINT>);
398
399impl Drop for EcPoint {
400 fn drop(&mut self) {
401 unsafe { EC_POINT_free(self.0.as_mut()) }
402 }
403}
404
405impl EcPoint {
406 pub fn new(group: &EcGroup) -> Result<Self, Error> {
407 ptr_or_error(unsafe { EC_POINT_new(group.0.as_ptr()) }).map(Self)
408 }
409
410 pub fn new_from_affine_coords(
412 x: Bignum,
413 y: Bignum,
414 group: &EcGroup,
415 ctx: &BignumCtx,
416 ) -> Result<Self, Error> {
417 let point = Self::new(group)?;
418 one_or_error(unsafe {
419 EC_POINT_set_affine_coordinates_GFp(
420 group.0.as_ptr(),
421 point.0.as_ptr(),
422 x.0.as_ptr(),
423 y.0.as_ptr(),
424 ctx.0.as_ptr(),
425 )
426 })?;
427 Ok(point)
428 }
429
430 pub fn to_affine_coords(
432 &self,
433 group: &EcGroup,
434 ctx: &BignumCtx,
435 ) -> Result<(Bignum, Bignum), Error> {
436 let x = Bignum::new()?;
437 let y = Bignum::new()?;
438 one_or_error(unsafe {
439 EC_POINT_get_affine_coordinates_GFp(
440 group.0.as_ptr(),
441 self.0.as_ptr(),
442 x.0.as_ptr(),
443 y.0.as_ptr(),
444 ctx.0.as_ptr(),
445 )
446 })?;
447 Ok((x, y))
448 }
449
450 pub fn mul(&self, group: &EcGroup, m: &Bignum, ctx: &BignumCtx) -> Result<EcPoint, Error> {
453 let result = Self::new(group)?;
454 one_or_error(unsafe {
455 EC_POINT_mul(
456 group.0.as_ptr(),
457 result.0.as_ptr(),
458 std::ptr::null_mut(),
459 self.0.as_ptr(),
460 m.0.as_ptr(),
461 ctx.0.as_ptr(),
462 )
463 })?;
464 Ok(result)
465 }
466
467 pub fn add(&self, group: &EcGroup, b: &EcPoint, ctx: &BignumCtx) -> Result<EcPoint, Error> {
470 let result = Self::new(group)?;
471 one_or_error(unsafe {
472 EC_POINT_add(
473 group.0.as_ptr(),
474 result.0.as_ptr(),
475 self.0.as_ptr(),
476 b.0.as_ptr(),
477 ctx.0.as_ptr(),
478 )
479 })?;
480 Ok(result)
481 }
482
483 pub fn invert(self, group: &EcGroup, ctx: &BignumCtx) -> Result<EcPoint, Error> {
485 one_or_error(unsafe {
486 EC_POINT_invert(group.0.as_ptr(), self.0.as_ptr(), ctx.0.as_ptr())
487 })?;
488 Ok(self)
489 }
490
491 pub fn is_point_at_infinity(&self, group: &EcGroup) -> bool {
493 unsafe { EC_POINT_is_at_infinity(group.0.as_ptr(), self.0.as_ptr()) == 1 }
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500
501 fn bn(value: &str) -> Bignum {
502 Bignum::new_from_string(value).unwrap()
503 }
504
505 #[test]
506 fn bignum_lifetime() {
507 for _ in 0..10 {
509 let bignum = Bignum::new().unwrap();
510 std::mem::drop(bignum);
511 }
512 }
513
514 #[test]
515 fn bignum_new() {
516 assert_eq!(Bignum::new_from_string("100").unwrap(), bn("100"));
517 assert_eq!(Bignum::new_from_u64(100).unwrap(), bn("100"));
518 assert_eq!(Bignum::new_from_slice(&[0xff, 0xff][..]).unwrap(), bn("65535"));
519 }
520
521 #[test]
522 fn bignum_set_negative() {
523 assert_eq!(bn("100").set_negative(), bn("-100"));
524 assert_eq!(bn("-100").set_negative(), bn("-100"));
525 assert_eq!(bn("0").set_negative(), bn("0"));
526 }
527
528 #[test]
529 fn bignum_format() {
530 assert_eq!(format!("{}", bn("100")), "100");
532 assert_eq!(format!("{}", bn("0x100")), "256");
533 }
534
535 #[test]
536 fn bignum_add() {
537 let bn1 = bn("1000000000000000000000");
538 let bn2 = bn("1000000000001234567890");
539 let sum = bn1.add(bn2).unwrap();
540 assert_eq!(sum, bn("2000000000001234567890"));
541
542 let bn1 = bn("-1000000000000000000000");
543 let bn2 = bn("1000000000001234567890");
544 let sum = bn1.add(bn2).unwrap();
545 assert_eq!(sum, bn("1234567890"));
546 }
547
548 #[test]
549 fn bignum_sub() {
550 let bn1 = bn("3000000000000987654321");
551 let bn2 = bn("2000000000000000000000");
552 let diff = bn1.sub(bn2).unwrap();
553 assert_eq!(diff, bn("1000000000000987654321"));
554
555 let bn1 = bn("2000000000000012345678");
556 let bn2 = bn("-3000000000000987654321");
557 let diff = bn1.sub(bn2).unwrap();
558 assert_eq!(diff, bn("5000000000000999999999"));
559 }
560
561 #[test]
562 fn bignum_mod_nonnegative() {
563 let ctx = BignumCtx::new().unwrap();
564 let bn1 = bn("12");
565 let bn2 = bn("5");
566 let mod_nonnegative = bn1.mod_nonnegative(&bn2, &ctx).unwrap();
567 assert_eq!(mod_nonnegative, bn("2"));
568
569 let bn1 = bn("-12");
570 let bn2 = bn("5");
571 let mod_nonnegative = bn1.mod_nonnegative(&bn2, &ctx).unwrap();
572 assert_eq!(mod_nonnegative, bn("3"));
573 }
574
575 #[test]
576 fn bignum_mod_add() {
577 let ctx = BignumCtx::new().unwrap();
578 let bn1 = bn("1000000000000000000000");
579 let bn2 = bn("1000000000001234567890");
580 let m = bn("2000000000000000000000");
581 let value = bn1.mod_add(&bn2, &m, &ctx).unwrap();
582 assert_eq!(value, bn("1234567890"));
583 }
584
585 #[test]
586 fn bignum_mod_mul() {
587 let ctx = BignumCtx::new().unwrap();
588 let value = bn("4").mod_mul(&bn("5"), &bn("12"), &ctx).unwrap();
589 assert_eq!(value, bn("8"));
590 }
591
592 #[test]
593 fn bignum_mod_inverse() {
594 let ctx = BignumCtx::new().unwrap();
595 assert_eq!(bn("3").mod_inverse(&bn("7"), &ctx).unwrap(), bn("5"));
596 }
597
598 #[test]
599 fn bignum_mod_exp() {
600 let ctx = BignumCtx::new().unwrap();
601 let value = bn("4").mod_exp(&bn("2"), &bn("10"), &ctx).unwrap();
602 assert_eq!(value, bn("6"));
603 }
604
605 #[test]
606 fn bigum_mod_square() {
607 let ctx = BignumCtx::new().unwrap();
608 assert_eq!(bn("11").mod_square(&bn("17"), &ctx).unwrap(), bn("2"));
609 }
610
611 #[test]
612 fn bignum_mod_sqrt() {
613 let ctx = BignumCtx::new().unwrap();
614 let m = bn("13"); let quadratic_residues = [1, 3, 4, 9, 10, 12];
617 for i in 1..12 {
618 let i_bn = Bignum::new_from_u64(i).unwrap();
619 let sqrt = i_bn.mod_sqrt(&m, &ctx);
620 if quadratic_residues.contains(&i) {
621 assert!(sqrt.is_ok());
622 assert_eq!(sqrt.unwrap().mod_exp(&bn("2"), &m, &ctx).unwrap(), i_bn);
623 } else {
624 assert!(sqrt.is_err());
625 }
626 }
627 }
628
629 #[test]
630 fn bignum_mod_sqrt_non_prime() {
631 let ctx = BignumCtx::new().unwrap();
632 let m = bn("100");
633 assert!(bn("16").mod_sqrt(&m, &ctx).is_err())
635 }
636
637 #[test]
638 fn bignum_rshift1() {
639 assert_eq!(bn("100").rshift1().unwrap(), bn("50"));
640 assert_eq!(bn("101").rshift1().unwrap(), bn("50"));
641 }
642
643 #[test]
644 fn bignum_simple_fns() {
645 assert!(bn("1").is_one());
646 assert!(!bn("100000").is_one());
647 assert!(Bignum::one().unwrap().is_one());
648
649 assert!(bn("0").is_zero());
650 assert!(!bn("1").is_zero());
651 assert!(Bignum::zero().unwrap().is_zero());
652
653 assert!(bn("1000001").is_odd());
654 assert!(!bn("1000002").is_odd());
655 }
656
657 #[test]
658 fn bignum_ord() {
659 let neg = bn("-100");
660 let zero = bn("0");
661 let pos = bn("100");
662
663 assert!(neg < zero);
664 assert!(pos > neg);
665 assert_eq!(neg, neg);
666 assert_eq!(zero, zero);
667 assert_eq!(pos, pos);
668 }
669
670 #[test]
671 fn bignum_to_be_vec() {
672 assert_eq!(bn("0xff00").to_be_vec(1), vec![0xff, 0x00]);
673 assert_eq!(bn("0xff00").to_be_vec(4), vec![0x00, 0x00, 0xff, 0x00]);
674 assert_eq!(bn("0").to_be_vec(4), vec![0x00, 0x00, 0x00, 0x00]);
675 }
676
677 const P: &'static str = "0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF";
679 const B: &'static str = "0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B";
680 const ORDER: &'static str =
681 "0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551";
682 const GX: &'static str = "0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296";
683 const GY: &'static str = "0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5";
684
685 const I: &'static str = "0xC88F01F510D9AC3F70A292DAA2316DE544E9AAB8AFE84049C62A9C57862D1433";
687 const GIX: &'static str = "0xDAD0B65394221CF9B051E1FECA5787D098DFE637FC90B9EF945D0C3772581180";
688 const GIY: &'static str = "0x5271A0461CDB8252D61F1C456FA3E59AB1F45B33ACCF5F58389E0577B8990BB3";
689
690 #[test]
691 fn ec_group_params() {
692 let group = EcGroup::new(EcGroupId::P256).unwrap();
693 let ctx = BignumCtx::new().unwrap();
694 let params = group.get_params(&ctx).unwrap();
695 let order = group.get_order(&ctx).unwrap();
696
697 assert_eq!(params.p, bn(P));
699 assert_eq!(params.a, params.p.sub(bn("3")).unwrap());
701 assert_eq!(params.b, bn(B));
702 assert_eq!(order, bn(ORDER));
703 }
704
705 #[test]
706 fn ec_point_to_coords() {
707 let group = EcGroup::new(EcGroupId::P256).unwrap();
708 let ctx = BignumCtx::new().unwrap();
709 let point = EcPoint::new_from_affine_coords(bn(GIX), bn(GIY), &group, &ctx).unwrap();
710 let (x, y) = point.to_affine_coords(&group, &ctx).unwrap();
711 assert_eq!(x, bn(GIX));
712 assert_eq!(y, bn(GIY));
713 }
714
715 #[test]
716 fn ec_wrong_coords_to_point_err() {
717 let group = EcGroup::new(EcGroupId::P256).unwrap();
718 let ctx = BignumCtx::new().unwrap();
719 let result =
720 EcPoint::new_from_affine_coords(bn(GIX).add(bn("1")).unwrap(), bn(GIY), &group, &ctx);
721 let Err(err) = result else { panic!("Expected error") };
722 assert!(format!("{:?}", err).contains("POINT_IS_NOT_ON_CURVE"));
723 }
724
725 #[test]
726 fn ec_point_mul() {
727 let group = EcGroup::new(EcGroupId::P256).unwrap();
728 let ctx = BignumCtx::new().unwrap();
729 let g = EcPoint::new_from_affine_coords(bn(GX), bn(GY), &group, &ctx).unwrap();
730 let gi = g.mul(&group, &bn(I), &ctx).unwrap();
731 let (gix, giy) = gi.to_affine_coords(&group, &ctx).unwrap();
732 assert_eq!(gix, bn(GIX));
733 assert_eq!(giy, bn(GIY));
734 }
735}