1use crate::ffi;
19use crate::libc_types::c_int;
20use foreign_types::{ForeignType, ForeignTypeRef};
21use openssl_macros::corresponds;
22use std::fmt;
23use std::ptr;
24
25use crate::bn::{BigNumContextRef, BigNumRef};
26use crate::error::ErrorStack;
27use crate::nid::Nid;
28use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public};
29use crate::{cvt, cvt_n, cvt_p, init};
30
31#[derive(Copy, Clone)]
41pub struct PointConversionForm(ffi::point_conversion_form_t);
42
43impl PointConversionForm {
44 pub const COMPRESSED: PointConversionForm =
46 PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_COMPRESSED);
47
48 pub const UNCOMPRESSED: PointConversionForm =
50 PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED);
51
52 pub const HYBRID: PointConversionForm =
54 PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_HYBRID);
55}
56
57#[derive(Copy, Clone)]
61pub struct Asn1Flag(c_int);
62
63impl Asn1Flag {
64 pub const EXPLICIT_CURVE: Asn1Flag = Asn1Flag(0);
78
79 pub const NAMED_CURVE: Asn1Flag = Asn1Flag(ffi::OPENSSL_EC_NAMED_CURVE);
88}
89
90foreign_type_and_impl_send_sync! {
91 type CType = ffi::EC_GROUP;
92 fn drop = ffi::EC_GROUP_free;
93
94 pub struct EcGroup;
111}
112
113impl EcGroup {
114 #[corresponds(EC_GROUP_new)]
116 pub fn from_curve_name(nid: Nid) -> Result<EcGroup, ErrorStack> {
117 unsafe {
118 init();
119 cvt_p(ffi::EC_GROUP_new_by_curve_name(nid.as_raw())).map(|p| EcGroup::from_ptr(p))
120 }
121 }
122}
123
124impl EcGroupRef {
125 pub fn components_gfp(
132 &self,
133 p: &mut BigNumRef,
134 a: &mut BigNumRef,
135 b: &mut BigNumRef,
136 ctx: &mut BigNumContextRef,
137 ) -> Result<(), ErrorStack> {
138 unsafe {
139 cvt(ffi::EC_GROUP_get_curve_GFp(
140 self.as_ptr(),
141 p.as_ptr(),
142 a.as_ptr(),
143 b.as_ptr(),
144 ctx.as_ptr(),
145 ))
146 }
147 }
148
149 #[corresponds(EC_GROUP_get_cofactor)]
151 pub fn cofactor(
152 &self,
153 cofactor: &mut BigNumRef,
154 ctx: &mut BigNumContextRef,
155 ) -> Result<(), ErrorStack> {
156 unsafe {
157 cvt(ffi::EC_GROUP_get_cofactor(
158 self.as_ptr(),
159 cofactor.as_ptr(),
160 ctx.as_ptr(),
161 ))
162 }
163 }
164
165 #[corresponds(EC_GROUP_get_degree)]
167 #[allow(clippy::unnecessary_cast)]
168 #[must_use]
169 pub fn degree(&self) -> u32 {
170 unsafe { ffi::EC_GROUP_get_degree(self.as_ptr()) as u32 }
171 }
172
173 #[corresponds(EC_GROUP_order_bits)]
175 #[must_use]
176 pub fn order_bits(&self) -> u32 {
177 unsafe { ffi::EC_GROUP_order_bits(self.as_ptr()) as u32 }
178 }
179
180 #[corresponds(EC_GROUP_get0_generator)]
182 #[must_use]
183 pub fn generator(&self) -> &EcPointRef {
184 unsafe {
185 let ptr = ffi::EC_GROUP_get0_generator(self.as_ptr());
186 EcPointRef::from_ptr(ptr as *mut _)
187 }
188 }
189
190 #[corresponds(EC_GROUP_get_order)]
192 pub fn order(
193 &self,
194 order: &mut BigNumRef,
195 ctx: &mut BigNumContextRef,
196 ) -> Result<(), ErrorStack> {
197 unsafe {
198 cvt(ffi::EC_GROUP_get_order(
199 self.as_ptr(),
200 order.as_ptr(),
201 ctx.as_ptr(),
202 ))
203 }
204 }
205
206 pub fn set_asn1_flag(&mut self, flag: Asn1Flag) {
212 unsafe {
213 ffi::EC_GROUP_set_asn1_flag(self.as_ptr(), flag.0);
214 }
215 }
216
217 #[corresponds(EC_GROUP_get_curve_name)]
219 #[must_use]
220 pub fn curve_name(&self) -> Option<Nid> {
221 let nid = unsafe { ffi::EC_GROUP_get_curve_name(self.as_ptr()) };
222 if nid > 0 {
223 Some(Nid::from_raw(nid))
224 } else {
225 None
226 }
227 }
228}
229
230foreign_type_and_impl_send_sync! {
231 type CType = ffi::EC_POINT;
232 fn drop = ffi::EC_POINT_free;
233
234 pub struct EcPoint;
240}
241
242impl EcPointRef {
243 #[corresponds(EC_POINT_add)]
245 pub fn add(
246 &mut self,
247 group: &EcGroupRef,
248 a: &EcPointRef,
249 b: &EcPointRef,
250 ctx: &mut BigNumContextRef,
251 ) -> Result<(), ErrorStack> {
252 unsafe {
253 cvt(ffi::EC_POINT_add(
254 group.as_ptr(),
255 self.as_ptr(),
256 a.as_ptr(),
257 b.as_ptr(),
258 ctx.as_ptr(),
259 ))
260 }
261 }
262
263 #[corresponds(EC_POINT_mul)]
265 pub fn mul(
266 &mut self,
267 group: &EcGroupRef,
268 q: &EcPointRef,
269 m: &BigNumRef,
270 ctx: &mut BigNumContextRef,
271 ) -> Result<(), ErrorStack> {
272 unsafe {
273 cvt(ffi::EC_POINT_mul(
274 group.as_ptr(),
275 self.as_ptr(),
276 ptr::null(),
277 q.as_ptr(),
278 m.as_ptr(),
279 ctx.as_ptr(),
280 ))
281 }
282 }
283
284 pub fn mul_generator(
286 &mut self,
287 group: &EcGroupRef,
288 n: &BigNumRef,
289 ctx: &mut BigNumContextRef,
290 ) -> Result<(), ErrorStack> {
291 unsafe {
292 cvt(ffi::EC_POINT_mul(
293 group.as_ptr(),
294 self.as_ptr(),
295 n.as_ptr(),
296 ptr::null(),
297 ptr::null(),
298 ctx.as_ptr(),
299 ))
300 }
301 }
302
303 pub fn mul_full(
305 &mut self,
306 group: &EcGroupRef,
307 n: &BigNumRef,
308 q: &EcPointRef,
309 m: &BigNumRef,
310 ctx: &mut BigNumContextRef,
311 ) -> Result<(), ErrorStack> {
312 unsafe {
313 cvt(ffi::EC_POINT_mul(
314 group.as_ptr(),
315 self.as_ptr(),
316 n.as_ptr(),
317 q.as_ptr(),
318 m.as_ptr(),
319 ctx.as_ptr(),
320 ))
321 }
322 }
323
324 #[corresponds(EC_POINT_invert)]
326 pub fn invert(&mut self, group: &EcGroupRef, ctx: &BigNumContextRef) -> Result<(), ErrorStack> {
327 unsafe {
328 cvt(ffi::EC_POINT_invert(
329 group.as_ptr(),
330 self.as_ptr(),
331 ctx.as_ptr(),
332 ))
333 }
334 }
335
336 #[corresponds(EC_POINT_point2oct)]
338 pub fn to_bytes(
339 &self,
340 group: &EcGroupRef,
341 form: PointConversionForm,
342 ctx: &mut BigNumContextRef,
343 ) -> Result<Vec<u8>, ErrorStack> {
344 unsafe {
345 let len = ffi::EC_POINT_point2oct(
346 group.as_ptr(),
347 self.as_ptr(),
348 form.0,
349 ptr::null_mut(),
350 0,
351 ctx.as_ptr(),
352 );
353 if len == 0 {
354 return Err(ErrorStack::get());
355 }
356 let mut buf = vec![0; len];
357 let len = ffi::EC_POINT_point2oct(
358 group.as_ptr(),
359 self.as_ptr(),
360 form.0,
361 buf.as_mut_ptr(),
362 len,
363 ctx.as_ptr(),
364 );
365 if len == 0 {
366 Err(ErrorStack::get())
367 } else {
368 Ok(buf)
369 }
370 }
371 }
372
373 #[corresponds(EC_POINT_dup)]
375 pub fn to_owned(&self, group: &EcGroupRef) -> Result<EcPoint, ErrorStack> {
376 unsafe {
377 cvt_p(ffi::EC_POINT_dup(self.as_ptr(), group.as_ptr())).map(|p| EcPoint::from_ptr(p))
378 }
379 }
380
381 pub fn eq(
387 &self,
388 group: &EcGroupRef,
389 other: &EcPointRef,
390 ctx: &mut BigNumContextRef,
391 ) -> Result<bool, ErrorStack> {
392 unsafe {
393 let res = cvt_n(ffi::EC_POINT_cmp(
394 group.as_ptr(),
395 self.as_ptr(),
396 other.as_ptr(),
397 ctx.as_ptr(),
398 ))?;
399 Ok(res == 0)
400 }
401 }
402
403 #[corresponds(EC_POINT_get_affine_coordinates_GFp)]
406 pub fn affine_coordinates_gfp(
407 &self,
408 group: &EcGroupRef,
409 x: &mut BigNumRef,
410 y: &mut BigNumRef,
411 ctx: &mut BigNumContextRef,
412 ) -> Result<(), ErrorStack> {
413 unsafe {
414 cvt(ffi::EC_POINT_get_affine_coordinates_GFp(
415 group.as_ptr(),
416 self.as_ptr(),
417 x.as_ptr(),
418 y.as_ptr(),
419 ctx.as_ptr(),
420 ))
421 }
422 }
423}
424
425impl EcPoint {
426 #[corresponds(EC_POINT_new)]
428 pub fn new(group: &EcGroupRef) -> Result<EcPoint, ErrorStack> {
429 unsafe { cvt_p(ffi::EC_POINT_new(group.as_ptr())).map(|p| EcPoint::from_ptr(p)) }
430 }
431
432 #[corresponds(EC_POINT_oct2point)]
434 pub fn from_bytes(
435 group: &EcGroupRef,
436 buf: &[u8],
437 ctx: &mut BigNumContextRef,
438 ) -> Result<EcPoint, ErrorStack> {
439 let point = EcPoint::new(group)?;
440 unsafe {
441 cvt(ffi::EC_POINT_oct2point(
442 group.as_ptr(),
443 point.as_ptr(),
444 buf.as_ptr(),
445 buf.len(),
446 ctx.as_ptr(),
447 ))?;
448 }
449 Ok(point)
450 }
451}
452
453generic_foreign_type_and_impl_send_sync! {
454 type CType = ffi::EC_KEY;
455 fn drop = ffi::EC_KEY_free;
456
457 pub struct EcKey<T>;
460
461 pub struct EcKeyRef<T>;
465}
466
467impl<T> EcKeyRef<T>
468where
469 T: HasPrivate,
470{
471 private_key_to_pem! {
472 #[corresponds(PEM_write_bio_ECPrivateKey)]
476 private_key_to_pem,
477 #[corresponds(PEM_write_bio_ECPrivateKey)]
481 private_key_to_pem_passphrase,
482 ffi::PEM_write_bio_ECPrivateKey
483 }
484
485 to_der! {
486 #[corresponds(i2d_ECPrivateKey)]
488 private_key_to_der,
489 ffi::i2d_ECPrivateKey
490 }
491
492 #[corresponds(EC_KEY_get0_private_key)]
494 #[must_use]
495 pub fn private_key(&self) -> &BigNumRef {
496 unsafe {
497 let ptr = ffi::EC_KEY_get0_private_key(self.as_ptr());
498 BigNumRef::from_ptr(ptr.cast_mut())
499 }
500 }
501}
502
503impl<T> EcKeyRef<T>
504where
505 T: HasPublic,
506{
507 #[corresponds(EC_KEY_get0_public_key)]
509 #[must_use]
510 pub fn public_key(&self) -> &EcPointRef {
511 unsafe {
512 let ptr = ffi::EC_KEY_get0_public_key(self.as_ptr());
513 EcPointRef::from_ptr(ptr.cast_mut())
514 }
515 }
516
517 to_pem! {
518 #[corresponds(PEM_write_bio_EC_PUBKEY)]
522 public_key_to_pem,
523 ffi::PEM_write_bio_EC_PUBKEY
524 }
525
526 to_der! {
527 #[corresponds(i2d_EC_PUBKEY)]
529 public_key_to_der,
530 ffi::i2d_EC_PUBKEY
531 }
532}
533
534impl<T> EcKeyRef<T>
535where
536 T: HasParams,
537{
538 #[corresponds(EC_KEY_get0_group)]
540 #[must_use]
541 pub fn group(&self) -> &EcGroupRef {
542 unsafe {
543 let ptr = ffi::EC_KEY_get0_group(self.as_ptr());
544 EcGroupRef::from_ptr(ptr.cast_mut())
545 }
546 }
547
548 #[corresponds(EC_KEY_check_key)]
550 pub fn check_key(&self) -> Result<(), ErrorStack> {
551 unsafe { cvt(ffi::EC_KEY_check_key(self.as_ptr())) }
552 }
553}
554
555impl<T> ToOwned for EcKeyRef<T> {
556 type Owned = EcKey<T>;
557
558 fn to_owned(&self) -> EcKey<T> {
559 unsafe {
560 let r = ffi::EC_KEY_up_ref(self.as_ptr());
561 assert!(r == 1);
562 EcKey::from_ptr(self.as_ptr())
563 }
564 }
565}
566
567impl EcKey<Params> {
568 #[corresponds(EC_KEY_new_by_curve_name)]
573 pub fn from_curve_name(nid: Nid) -> Result<EcKey<Params>, ErrorStack> {
574 unsafe {
575 init();
576 cvt_p(ffi::EC_KEY_new_by_curve_name(nid.as_raw())).map(|p| EcKey::from_ptr(p))
577 }
578 }
579
580 #[corresponds(EC_KEY_set_group)]
582 pub fn from_group(group: &EcGroupRef) -> Result<EcKey<Params>, ErrorStack> {
583 unsafe {
584 cvt_p(ffi::EC_KEY_new())
585 .map(|p| EcKey::from_ptr(p))
586 .and_then(|key| {
587 cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key)
588 })
589 }
590 }
591}
592
593impl EcKey<Public> {
594 pub fn from_public_key(
616 group: &EcGroupRef,
617 public_key: &EcPointRef,
618 ) -> Result<EcKey<Public>, ErrorStack> {
619 unsafe {
620 cvt_p(ffi::EC_KEY_new())
621 .map(|p| EcKey::from_ptr(p))
622 .and_then(|key| {
623 cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key)
624 })
625 .and_then(|key| {
626 cvt(ffi::EC_KEY_set_public_key(
627 key.as_ptr(),
628 public_key.as_ptr(),
629 ))
630 .map(|_| key)
631 })
632 }
633 }
634
635 pub fn from_public_key_affine_coordinates(
637 group: &EcGroupRef,
638 x: &BigNumRef,
639 y: &BigNumRef,
640 ) -> Result<EcKey<Public>, ErrorStack> {
641 unsafe {
642 cvt_p(ffi::EC_KEY_new())
643 .map(|p| EcKey::from_ptr(p))
644 .and_then(|key| {
645 cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key)
646 })
647 .and_then(|key| {
648 cvt(ffi::EC_KEY_set_public_key_affine_coordinates(
649 key.as_ptr(),
650 x.as_ptr(),
651 y.as_ptr(),
652 ))
653 .map(|_| key)
654 })
655 }
656 }
657
658 from_pem! {
659 #[corresponds(PEM_read_bio_EC_PUBKEY)]
663 public_key_from_pem,
664 EcKey<Public>,
665 ffi::PEM_read_bio_EC_PUBKEY
666 }
667
668 from_der! {
669 #[corresponds(d2i_EC_PUBKEY)]
671 public_key_from_der,
672 EcKey<Public>,
673 ffi::d2i_EC_PUBKEY,
674 crate::libc_types::c_long
675 }
676}
677
678impl EcKey<Private> {
679 pub fn generate(group: &EcGroupRef) -> Result<EcKey<Private>, ErrorStack> {
681 unsafe {
682 cvt_p(ffi::EC_KEY_new())
683 .map(|p| EcKey::from_ptr(p))
684 .and_then(|key| {
685 cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key)
686 })
687 .and_then(|key| cvt(ffi::EC_KEY_generate_key(key.as_ptr())).map(|_| key))
688 }
689 }
690
691 pub fn from_private_components(
693 group: &EcGroupRef,
694 private_number: &BigNumRef,
695 public_key: &EcPointRef,
696 ) -> Result<EcKey<Private>, ErrorStack> {
697 unsafe {
698 cvt_p(ffi::EC_KEY_new())
699 .map(|p| EcKey::from_ptr(p))
700 .and_then(|key| {
701 cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key)
702 })
703 .and_then(|key| {
704 cvt(ffi::EC_KEY_set_private_key(
705 key.as_ptr(),
706 private_number.as_ptr(),
707 ))
708 .map(|_| key)
709 })
710 .and_then(|key| {
711 cvt(ffi::EC_KEY_set_public_key(
712 key.as_ptr(),
713 public_key.as_ptr(),
714 ))
715 .map(|_| key)
716 })
717 }
718 }
719
720 private_key_from_pem! {
721 #[corresponds(PEM_read_bio_ECPrivateKey)]
725 private_key_from_pem,
726
727 #[corresponds(PEM_read_bio_ECPrivateKey)]
731 private_key_from_pem_passphrase,
732
733 #[corresponds(PEM_read_bio_ECPrivateKey)]
739 private_key_from_pem_callback,
740 EcKey<Private>,
741 ffi::PEM_read_bio_ECPrivateKey
742 }
743
744 from_der! {
745 #[corresponds(d2i_ECPrivateKey)]
747 private_key_from_der,
748 EcKey<Private>,
749 ffi::d2i_ECPrivateKey,
750 crate::libc_types::c_long
751 }
752}
753
754impl<T> Clone for EcKey<T> {
755 fn clone(&self) -> EcKey<T> {
756 (**self).to_owned()
757 }
758}
759
760impl<T> fmt::Debug for EcKey<T> {
761 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
762 write!(f, "EcKey")
763 }
764}
765
766#[cfg(test)]
767mod test {
768 use hex::FromHex;
769
770 use super::*;
771 use crate::bn::{BigNum, BigNumContext};
772 use crate::nid::Nid;
773
774 #[test]
775 fn key_new_by_curve_name() {
776 EcKey::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
777 }
778
779 #[test]
780 fn generate() {
781 let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
782 EcKey::generate(&group).unwrap();
783 }
784
785 #[test]
786 fn cofactor() {
787 let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
788 let mut ctx = BigNumContext::new().unwrap();
789 let mut cofactor = BigNum::new().unwrap();
790 group.cofactor(&mut cofactor, &mut ctx).unwrap();
791 let one = BigNum::from_u32(1).unwrap();
792 assert_eq!(cofactor, one);
793 }
794
795 #[test]
796 #[allow(clippy::redundant_clone)]
797 fn dup() {
798 let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
799 let key = EcKey::generate(&group).unwrap();
800 drop(key.clone());
801 }
802
803 #[test]
804 fn point_new() {
805 let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
806 EcPoint::new(&group).unwrap();
807 }
808
809 #[test]
810 fn point_bytes() {
811 let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
812 let key = EcKey::generate(&group).unwrap();
813 let point = key.public_key();
814 let mut ctx = BigNumContext::new().unwrap();
815 let bytes = point
816 .to_bytes(&group, PointConversionForm::COMPRESSED, &mut ctx)
817 .unwrap();
818 let point2 = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap();
819 assert!(point.eq(&group, &point2, &mut ctx).unwrap());
820 }
821
822 #[test]
823 fn point_owned() {
824 let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
825 let key = EcKey::generate(&group).unwrap();
826 let point = key.public_key();
827 let owned = point.to_owned(&group).unwrap();
828 let mut ctx = BigNumContext::new().unwrap();
829 assert!(owned.eq(&group, point, &mut ctx).unwrap());
830 }
831
832 #[test]
833 fn mul_generator() {
834 let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
835 let key = EcKey::generate(&group).unwrap();
836 let mut ctx = BigNumContext::new().unwrap();
837 let mut public_key = EcPoint::new(&group).unwrap();
838 public_key
839 .mul_generator(&group, key.private_key(), &mut ctx)
840 .unwrap();
841 assert!(public_key.eq(&group, key.public_key(), &mut ctx).unwrap());
842 }
843
844 #[test]
845 fn generator() {
846 let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
847 let generator = group.generator();
848 let one = BigNum::from_u32(1).unwrap();
849 let mut ctx = BigNumContext::new().unwrap();
850 let mut ecp = EcPoint::new(&group).unwrap();
851 ecp.mul_generator(&group, &one, &mut ctx).unwrap();
852 assert!(ecp.eq(&group, generator, &mut ctx).unwrap());
853 }
854
855 #[test]
856 fn key_from_public_key() {
857 let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
858 let key = EcKey::generate(&group).unwrap();
859 let mut ctx = BigNumContext::new().unwrap();
860 let bytes = key
861 .public_key()
862 .to_bytes(&group, PointConversionForm::COMPRESSED, &mut ctx)
863 .unwrap();
864
865 drop(key);
866 let public_key = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap();
867 let ec_key = EcKey::from_public_key(&group, &public_key).unwrap();
868 assert!(ec_key.check_key().is_ok());
869 }
870
871 #[test]
872 fn key_from_private_components() {
873 let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
874 let key = EcKey::generate(&group).unwrap();
875
876 let dup_key =
877 EcKey::from_private_components(&group, key.private_key(), key.public_key()).unwrap();
878 dup_key.check_key().unwrap();
879
880 assert!(key.private_key() == dup_key.private_key());
881 }
882
883 #[test]
884 fn key_from_affine_coordinates() {
885 let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
886 let x = Vec::from_hex("30a0424cd21c2944838a2d75c92b37e76ea20d9f00893a3b4eee8a3c0aafec3e")
887 .unwrap();
888 let y = Vec::from_hex("e04b65e92456d9888b52b379bdfbd51ee869ef1f0fc65b6659695b6cce081723")
889 .unwrap();
890
891 let xbn = BigNum::from_slice(&x).unwrap();
892 let ybn = BigNum::from_slice(&y).unwrap();
893
894 let ec_key = EcKey::from_public_key_affine_coordinates(&group, &xbn, &ybn).unwrap();
895 assert!(ec_key.check_key().is_ok());
896 }
897
898 #[test]
899 fn get_affine_coordinates() {
900 let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
901 let x = Vec::from_hex("30a0424cd21c2944838a2d75c92b37e76ea20d9f00893a3b4eee8a3c0aafec3e")
902 .unwrap();
903 let y = Vec::from_hex("e04b65e92456d9888b52b379bdfbd51ee869ef1f0fc65b6659695b6cce081723")
904 .unwrap();
905
906 let xbn = BigNum::from_slice(&x).unwrap();
907 let ybn = BigNum::from_slice(&y).unwrap();
908
909 let ec_key = EcKey::from_public_key_affine_coordinates(&group, &xbn, &ybn).unwrap();
910
911 let mut xbn2 = BigNum::new().unwrap();
912 let mut ybn2 = BigNum::new().unwrap();
913 let mut ctx = BigNumContext::new().unwrap();
914 let ec_key_pk = ec_key.public_key();
915 ec_key_pk
916 .affine_coordinates_gfp(&group, &mut xbn2, &mut ybn2, &mut ctx)
917 .unwrap();
918 assert_eq!(xbn2, xbn);
919 assert_eq!(ybn2, ybn);
920 }
921}