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