rama_boring/
ec.rs

1//! Elliptic Curve
2//!
3//! Cryptology relies on the difficulty of solving mathematical problems, such as the factor
4//! of large integers composed of two large prime numbers and the discrete logarithm of a
5//! random eliptic curve.  This module provides low-level features of the latter.
6//! Elliptic Curve protocols can provide the same security with smaller keys.
7//!
8//! There are 2 forms of elliptic curves, `Fp` and `F2^m`.  These curves use irreducible
9//! trinomial or pentanomial .  Being a generic interface to a wide range of algorithms,
10//! the cuves are generally referenced by [`EcGroup`].  There are many built in groups
11//! found in [`Nid`].
12//!
13//! OpenSSL Wiki explains the fields and curves in detail at [Eliptic Curve Cryptography].
14//!
15//! [`EcGroup`]: struct.EcGroup.html
16//! [`Nid`]: ../nid/struct.Nid.html
17//! [Eliptic Curve Cryptography]: https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography
18use crate::ffi;
19use foreign_types::{ForeignType, ForeignTypeRef};
20use libc::c_int;
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/// Compressed or Uncompressed conversion
32///
33/// Conversion from the binary value of the point on the curve is performed in one of
34/// compressed, uncompressed, or hybrid conversions.  The default is compressed, except
35/// for binary curves.
36///
37/// Further documentation is available in the [X9.62] standard.
38///
39/// [X9.62]: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf
40#[derive(Copy, Clone)]
41pub struct PointConversionForm(ffi::point_conversion_form_t);
42
43impl PointConversionForm {
44    /// Compressed conversion from point value.
45    pub const COMPRESSED: PointConversionForm =
46        PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_COMPRESSED);
47
48    /// Uncompressed conversion from point value.
49    pub const UNCOMPRESSED: PointConversionForm =
50        PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED);
51
52    /// Performs both compressed and uncompressed conversions.
53    pub const HYBRID: PointConversionForm =
54        PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_HYBRID);
55}
56
57/// Named Curve or Explicit
58///
59/// This type acts as a boolean as to whether the `EcGroup` is named or explicit.
60#[derive(Copy, Clone)]
61pub struct Asn1Flag(c_int);
62
63impl Asn1Flag {
64    /// Curve defined using polynomial parameters
65    ///
66    /// Most applications use a named EC_GROUP curve, however, support
67    /// is included to explicitly define the curve used to calculate keys
68    /// This information would need to be known by both endpoint to make communication
69    /// effective.
70    ///
71    /// OPENSSL_EC_EXPLICIT_CURVE, but that was only added in 1.1.
72    /// Man page documents that 0 can be used in older versions.
73    ///
74    /// OpenSSL documentation at [`EC_GROUP`]
75    ///
76    /// [`EC_GROUP`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_seed_len.html
77    pub const EXPLICIT_CURVE: Asn1Flag = Asn1Flag(0);
78
79    /// Standard Curves
80    ///
81    /// Curves that make up the typical encryption use cases.  The collection of curves
82    /// are well known but extensible.
83    ///
84    /// OpenSSL documentation at [`EC_GROUP`]
85    ///
86    /// [`EC_GROUP`]: https://www.openssl.org/docs/manmaster/man3/EC_GROUP_order_bits.html
87    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    /// Describes the curve
95    ///
96    /// A curve can be of the named curve type.  These curves can be discovered
97    /// using openssl binary `openssl ecparam -list_curves`.  Other operations
98    /// are available in the [wiki].  These named curves are available in the
99    /// [`Nid`] module.
100    ///
101    /// Curves can also be generated using prime field parameters or a binary field.
102    ///
103    /// Prime fields use the formula `y^2 mod p = x^3 + ax + b mod p`.  Binary
104    /// fields use the formula `y^2 + xy = x^3 + ax^2 + b`.  Named curves have
105    /// assured security.  To prevent accidental vulnerabilities, they should
106    /// be preferred.
107    ///
108    /// [wiki]: https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations
109    /// [`Nid`]: ../nid/index.html
110    pub struct EcGroup;
111}
112
113impl EcGroup {
114    /// Returns the group of a standard named curve.
115    #[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    /// Places the components of a curve over a prime field in the provided `BigNum`s.
126    /// The components make up the formula `y^2 mod p = x^3 + ax + b mod p`.
127    ///
128    /// OpenSSL documentation available at [`EC_GROUP_get_curve_GFp`]
129    ///
130    /// [`EC_GROUP_get_curve_GFp`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_curve_GFp.html
131    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            .map(|_| ())
147        }
148    }
149
150    /// Places the cofactor of the group in the provided `BigNum`.
151    #[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            .map(|_| ())
164        }
165    }
166
167    /// Returns the degree of the curve.
168    #[corresponds(EC_GROUP_get_degree)]
169    #[allow(clippy::unnecessary_cast)]
170    pub fn degree(&self) -> u32 {
171        unsafe { ffi::EC_GROUP_get_degree(self.as_ptr()) as u32 }
172    }
173
174    /// Returns the number of bits in the group order.
175    #[corresponds(EC_GROUP_order_bits)]
176    pub fn order_bits(&self) -> u32 {
177        unsafe { ffi::EC_GROUP_order_bits(self.as_ptr()) as u32 }
178    }
179
180    /// Returns the generator for the given curve as a [`EcPoint`].
181    #[corresponds(EC_GROUP_get0_generator)]
182    pub fn generator(&self) -> &EcPointRef {
183        unsafe {
184            let ptr = ffi::EC_GROUP_get0_generator(self.as_ptr());
185            EcPointRef::from_ptr(ptr as *mut _)
186        }
187    }
188
189    /// Places the order of the curve in the provided `BigNum`.
190    #[corresponds(EC_GROUP_get_order)]
191    pub fn order(
192        &self,
193        order: &mut BigNumRef,
194        ctx: &mut BigNumContextRef,
195    ) -> Result<(), ErrorStack> {
196        unsafe {
197            cvt(ffi::EC_GROUP_get_order(
198                self.as_ptr(),
199                order.as_ptr(),
200                ctx.as_ptr(),
201            ))
202            .map(|_| ())
203        }
204    }
205
206    /// Sets the flag determining if the group corresponds to a named curve or must be explicitly
207    /// parameterized.
208    ///
209    /// This defaults to `EXPLICIT_CURVE` in OpenSSL 1.0.1 and 1.0.2, but `NAMED_CURVE` in OpenSSL
210    /// 1.1.0.
211    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    /// Returns the name of the curve, if a name is associated.
218    #[corresponds(EC_GROUP_get_curve_name)]
219    pub fn curve_name(&self) -> Option<Nid> {
220        let nid = unsafe { ffi::EC_GROUP_get_curve_name(self.as_ptr()) };
221        if nid > 0 {
222            Some(Nid::from_raw(nid))
223        } else {
224            None
225        }
226    }
227}
228
229foreign_type_and_impl_send_sync! {
230    type CType = ffi::EC_POINT;
231    fn drop = ffi::EC_POINT_free;
232
233    /// Represents a point on the curve
234    ///
235    /// OpenSSL documentation at [`EC_POINT_new`]
236    ///
237    /// [`EC_POINT_new`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_new.html
238    pub struct EcPoint;
239}
240
241impl EcPointRef {
242    /// Computes `a + b`, storing the result in `self`.
243    #[corresponds(EC_POINT_add)]
244    pub fn add(
245        &mut self,
246        group: &EcGroupRef,
247        a: &EcPointRef,
248        b: &EcPointRef,
249        ctx: &mut BigNumContextRef,
250    ) -> Result<(), ErrorStack> {
251        unsafe {
252            cvt(ffi::EC_POINT_add(
253                group.as_ptr(),
254                self.as_ptr(),
255                a.as_ptr(),
256                b.as_ptr(),
257                ctx.as_ptr(),
258            ))
259            .map(|_| ())
260        }
261    }
262
263    /// Computes `q * m`, storing the result in `self`.
264    #[corresponds(EC_POINT_mul)]
265    pub fn mul(
266        &mut self,
267        group: &EcGroupRef,
268        q: &EcPointRef,
269        m: &BigNumRef,
270        // FIXME should be &mut
271        ctx: &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            .map(|_| ())
283        }
284    }
285
286    /// Computes `generator * n`, storing the result in `self`.
287    pub fn mul_generator(
288        &mut self,
289        group: &EcGroupRef,
290        n: &BigNumRef,
291        // FIXME should be &mut
292        ctx: &BigNumContextRef,
293    ) -> Result<(), ErrorStack> {
294        unsafe {
295            cvt(ffi::EC_POINT_mul(
296                group.as_ptr(),
297                self.as_ptr(),
298                n.as_ptr(),
299                ptr::null(),
300                ptr::null(),
301                ctx.as_ptr(),
302            ))
303            .map(|_| ())
304        }
305    }
306
307    /// Computes `generator * n + q * m`, storing the result in `self`.
308    pub fn mul_full(
309        &mut self,
310        group: &EcGroupRef,
311        n: &BigNumRef,
312        q: &EcPointRef,
313        m: &BigNumRef,
314        ctx: &mut BigNumContextRef,
315    ) -> Result<(), ErrorStack> {
316        unsafe {
317            cvt(ffi::EC_POINT_mul(
318                group.as_ptr(),
319                self.as_ptr(),
320                n.as_ptr(),
321                q.as_ptr(),
322                m.as_ptr(),
323                ctx.as_ptr(),
324            ))
325            .map(|_| ())
326        }
327    }
328
329    /// Inverts `self`.
330    #[corresponds(EC_POINT_invert)]
331    pub fn invert(&mut self, group: &EcGroupRef, ctx: &BigNumContextRef) -> Result<(), ErrorStack> {
332        unsafe {
333            cvt(ffi::EC_POINT_invert(
334                group.as_ptr(),
335                self.as_ptr(),
336                ctx.as_ptr(),
337            ))
338            .map(|_| ())
339        }
340    }
341
342    /// Serializes the point to a binary representation.
343    #[corresponds(EC_POINT_point2oct)]
344    pub fn to_bytes(
345        &self,
346        group: &EcGroupRef,
347        form: PointConversionForm,
348        ctx: &mut BigNumContextRef,
349    ) -> Result<Vec<u8>, ErrorStack> {
350        unsafe {
351            let len = ffi::EC_POINT_point2oct(
352                group.as_ptr(),
353                self.as_ptr(),
354                form.0,
355                ptr::null_mut(),
356                0,
357                ctx.as_ptr(),
358            );
359            if len == 0 {
360                return Err(ErrorStack::get());
361            }
362            let mut buf = vec![0; len];
363            let len = ffi::EC_POINT_point2oct(
364                group.as_ptr(),
365                self.as_ptr(),
366                form.0,
367                buf.as_mut_ptr(),
368                len,
369                ctx.as_ptr(),
370            );
371            if len == 0 {
372                Err(ErrorStack::get())
373            } else {
374                Ok(buf)
375            }
376        }
377    }
378
379    /// Creates a new point on the specified curve with the same value.
380    #[corresponds(EC_POINT_dup)]
381    pub fn to_owned(&self, group: &EcGroupRef) -> Result<EcPoint, ErrorStack> {
382        unsafe {
383            cvt_p(ffi::EC_POINT_dup(self.as_ptr(), group.as_ptr())).map(|p| EcPoint::from_ptr(p))
384        }
385    }
386
387    /// Determines if this point is equal to another.
388    ///
389    /// OpenSSL doucmentation at [`EC_POINT_cmp`]
390    ///
391    /// [`EC_POINT_cmp`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_cmp.html
392    pub fn eq(
393        &self,
394        group: &EcGroupRef,
395        other: &EcPointRef,
396        ctx: &mut BigNumContextRef,
397    ) -> Result<bool, ErrorStack> {
398        unsafe {
399            let res = cvt_n(ffi::EC_POINT_cmp(
400                group.as_ptr(),
401                self.as_ptr(),
402                other.as_ptr(),
403                ctx.as_ptr(),
404            ))?;
405            Ok(res == 0)
406        }
407    }
408
409    /// Place affine coordinates of a curve over a prime field in the provided
410    /// `x` and `y` `BigNum`s
411    #[corresponds(EC_POINT_get_affine_coordinates_GFp)]
412    pub fn affine_coordinates_gfp(
413        &self,
414        group: &EcGroupRef,
415        x: &mut BigNumRef,
416        y: &mut BigNumRef,
417        ctx: &mut BigNumContextRef,
418    ) -> Result<(), ErrorStack> {
419        unsafe {
420            cvt(ffi::EC_POINT_get_affine_coordinates_GFp(
421                group.as_ptr(),
422                self.as_ptr(),
423                x.as_ptr(),
424                y.as_ptr(),
425                ctx.as_ptr(),
426            ))
427            .map(|_| ())
428        }
429    }
430}
431
432impl EcPoint {
433    /// Creates a new point on the specified curve.
434    #[corresponds(EC_POINT_new)]
435    pub fn new(group: &EcGroupRef) -> Result<EcPoint, ErrorStack> {
436        unsafe { cvt_p(ffi::EC_POINT_new(group.as_ptr())).map(|p| EcPoint::from_ptr(p)) }
437    }
438
439    /// Creates point from a binary representation
440    #[corresponds(EC_POINT_oct2point)]
441    pub fn from_bytes(
442        group: &EcGroupRef,
443        buf: &[u8],
444        ctx: &mut BigNumContextRef,
445    ) -> Result<EcPoint, ErrorStack> {
446        let point = EcPoint::new(group)?;
447        unsafe {
448            cvt(ffi::EC_POINT_oct2point(
449                group.as_ptr(),
450                point.as_ptr(),
451                buf.as_ptr(),
452                buf.len(),
453                ctx.as_ptr(),
454            ))?;
455        }
456        Ok(point)
457    }
458}
459
460generic_foreign_type_and_impl_send_sync! {
461    type CType = ffi::EC_KEY;
462    fn drop = ffi::EC_KEY_free;
463
464    /// Public and optional Private key on the given curve
465    ///
466    pub struct EcKey<T>;
467
468    /// Reference to [`EcKey`]
469    ///
470    /// [`EcKey`]: struct.EcKey.html
471    pub struct EcKeyRef<T>;
472}
473
474impl<T> EcKeyRef<T>
475where
476    T: HasPrivate,
477{
478    private_key_to_pem! {
479        /// Serializes the private key to a PEM-encoded ECPrivateKey structure.
480        ///
481        /// The output will have a header of `-----BEGIN EC PRIVATE KEY-----`.
482        #[corresponds(PEM_write_bio_ECPrivateKey)]
483        private_key_to_pem,
484        /// Serializes the private key to a PEM-encoded encrypted ECPrivateKey structure.
485        ///
486        /// The output will have a header of `-----BEGIN EC PRIVATE KEY-----`.
487        #[corresponds(PEM_write_bio_ECPrivateKey)]
488        private_key_to_pem_passphrase,
489        ffi::PEM_write_bio_ECPrivateKey
490    }
491
492    to_der! {
493        /// Serializes the private key into a DER-encoded ECPrivateKey structure.
494        #[corresponds(i2d_ECPrivateKey)]
495        private_key_to_der,
496        ffi::i2d_ECPrivateKey
497    }
498
499    /// Return [`EcPoint`] associated with the private key
500    #[corresponds(EC_KEY_get0_private_key)]
501    pub fn private_key(&self) -> &BigNumRef {
502        unsafe {
503            let ptr = ffi::EC_KEY_get0_private_key(self.as_ptr());
504            BigNumRef::from_ptr(ptr as *mut _)
505        }
506    }
507}
508
509impl<T> EcKeyRef<T>
510where
511    T: HasPublic,
512{
513    /// Returns the public key.
514    #[corresponds(EC_KEY_get0_public_key)]
515    pub fn public_key(&self) -> &EcPointRef {
516        unsafe {
517            let ptr = ffi::EC_KEY_get0_public_key(self.as_ptr());
518            EcPointRef::from_ptr(ptr as *mut _)
519        }
520    }
521
522    to_pem! {
523        /// Serialies the public key into a PEM-encoded SubjectPublicKeyInfo structure.
524        ///
525        /// The output will have a header of `-----BEGIN PUBLIC KEY-----`.
526        #[corresponds(PEM_write_bio_EC_PUBKEY)]
527        public_key_to_pem,
528        ffi::PEM_write_bio_EC_PUBKEY
529    }
530
531    to_der! {
532        /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure.
533        #[corresponds(i2d_EC_PUBKEY)]
534        public_key_to_der,
535        ffi::i2d_EC_PUBKEY
536    }
537}
538
539impl<T> EcKeyRef<T>
540where
541    T: HasParams,
542{
543    /// Return [`EcGroup`] of the `EcKey`
544    #[corresponds(EC_KEY_get0_group)]
545    pub fn group(&self) -> &EcGroupRef {
546        unsafe {
547            let ptr = ffi::EC_KEY_get0_group(self.as_ptr());
548            EcGroupRef::from_ptr(ptr as *mut _)
549        }
550    }
551
552    /// Checks the key for validity.
553    #[corresponds(EC_KEY_check_key)]
554    pub fn check_key(&self) -> Result<(), ErrorStack> {
555        unsafe { cvt(ffi::EC_KEY_check_key(self.as_ptr())).map(|_| ()) }
556    }
557}
558
559impl<T> ToOwned for EcKeyRef<T> {
560    type Owned = EcKey<T>;
561
562    fn to_owned(&self) -> EcKey<T> {
563        unsafe {
564            let r = ffi::EC_KEY_up_ref(self.as_ptr());
565            assert!(r == 1);
566            EcKey::from_ptr(self.as_ptr())
567        }
568    }
569}
570
571impl EcKey<Params> {
572    /// Constructs an `EcKey` corresponding to a known curve.
573    ///
574    /// It will not have an associated public or private key. This kind of key is primarily useful
575    /// to be provided to the `set_tmp_ecdh` methods on `Ssl` and `SslContextBuilder`.
576    #[corresponds(EC_KEY_new_by_curve_name)]
577    pub fn from_curve_name(nid: Nid) -> Result<EcKey<Params>, ErrorStack> {
578        unsafe {
579            init();
580            cvt_p(ffi::EC_KEY_new_by_curve_name(nid.as_raw())).map(|p| EcKey::from_ptr(p))
581        }
582    }
583
584    /// Constructs an `EcKey` corresponding to a curve.
585    #[corresponds(EC_KEY_set_group)]
586    pub fn from_group(group: &EcGroupRef) -> Result<EcKey<Params>, ErrorStack> {
587        unsafe {
588            cvt_p(ffi::EC_KEY_new())
589                .map(|p| EcKey::from_ptr(p))
590                .and_then(|key| {
591                    cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key)
592                })
593        }
594    }
595}
596
597impl EcKey<Public> {
598    /// Constructs an `EcKey` from the specified group with the associated `EcPoint`, public_key.
599    ///
600    /// This will only have the associated public_key.
601    ///
602    /// # Example
603    ///
604    /// ```no_run
605    /// use rama_boring::bn::BigNumContext;
606    /// use rama_boring::ec::*;
607    /// use rama_boring::nid::Nid;
608    /// use rama_boring::pkey::PKey;
609    ///
610    /// // get bytes from somewhere, i.e. this will not produce a valid key
611    /// let public_key: Vec<u8> = vec![];
612    ///
613    /// // create an EcKey from the binary form of a EcPoint
614    /// let group = EcGroup::from_curve_name(Nid::SECP256K1).unwrap();
615    /// let mut ctx = BigNumContext::new().unwrap();
616    /// let point = EcPoint::from_bytes(&group, &public_key, &mut ctx).unwrap();
617    /// let key = EcKey::from_public_key(&group, &point);
618    /// ```
619    pub fn from_public_key(
620        group: &EcGroupRef,
621        public_key: &EcPointRef,
622    ) -> Result<EcKey<Public>, ErrorStack> {
623        unsafe {
624            cvt_p(ffi::EC_KEY_new())
625                .map(|p| EcKey::from_ptr(p))
626                .and_then(|key| {
627                    cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key)
628                })
629                .and_then(|key| {
630                    cvt(ffi::EC_KEY_set_public_key(
631                        key.as_ptr(),
632                        public_key.as_ptr(),
633                    ))
634                    .map(|_| key)
635                })
636        }
637    }
638
639    /// Constructs a public key from its affine coordinates.
640    pub fn from_public_key_affine_coordinates(
641        group: &EcGroupRef,
642        x: &BigNumRef,
643        y: &BigNumRef,
644    ) -> Result<EcKey<Public>, ErrorStack> {
645        unsafe {
646            cvt_p(ffi::EC_KEY_new())
647                .map(|p| EcKey::from_ptr(p))
648                .and_then(|key| {
649                    cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key)
650                })
651                .and_then(|key| {
652                    cvt(ffi::EC_KEY_set_public_key_affine_coordinates(
653                        key.as_ptr(),
654                        x.as_ptr(),
655                        y.as_ptr(),
656                    ))
657                    .map(|_| key)
658                })
659        }
660    }
661
662    from_pem! {
663        /// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing a EC key.
664        ///
665        /// The input should have a header of `-----BEGIN PUBLIC KEY-----`.
666        #[corresponds(PEM_read_bio_EC_PUBKEY)]
667        public_key_from_pem,
668        EcKey<Public>,
669        ffi::PEM_read_bio_EC_PUBKEY
670    }
671
672    from_der! {
673        /// Decodes a DER-encoded SubjectPublicKeyInfo structure containing a EC key.
674        #[corresponds(d2i_EC_PUBKEY)]
675        public_key_from_der,
676        EcKey<Public>,
677        ffi::d2i_EC_PUBKEY,
678        ::libc::c_long
679    }
680}
681
682impl EcKey<Private> {
683    /// Generates a new public/private key pair on the specified curve.
684    pub fn generate(group: &EcGroupRef) -> Result<EcKey<Private>, ErrorStack> {
685        unsafe {
686            cvt_p(ffi::EC_KEY_new())
687                .map(|p| EcKey::from_ptr(p))
688                .and_then(|key| {
689                    cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key)
690                })
691                .and_then(|key| cvt(ffi::EC_KEY_generate_key(key.as_ptr())).map(|_| key))
692        }
693    }
694
695    /// Constructs an public/private key pair given a curve, a private key and a public key point.
696    pub fn from_private_components(
697        group: &EcGroupRef,
698        private_number: &BigNumRef,
699        public_key: &EcPointRef,
700    ) -> Result<EcKey<Private>, ErrorStack> {
701        unsafe {
702            cvt_p(ffi::EC_KEY_new())
703                .map(|p| EcKey::from_ptr(p))
704                .and_then(|key| {
705                    cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key)
706                })
707                .and_then(|key| {
708                    cvt(ffi::EC_KEY_set_private_key(
709                        key.as_ptr(),
710                        private_number.as_ptr(),
711                    ))
712                    .map(|_| key)
713                })
714                .and_then(|key| {
715                    cvt(ffi::EC_KEY_set_public_key(
716                        key.as_ptr(),
717                        public_key.as_ptr(),
718                    ))
719                    .map(|_| key)
720                })
721        }
722    }
723
724    private_key_from_pem! {
725        /// Deserializes a private key from a PEM-encoded ECPrivateKey structure.
726        ///
727        /// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`.
728        #[corresponds(PEM_read_bio_ECPrivateKey)]
729        private_key_from_pem,
730
731        /// Deserializes a private key from a PEM-encoded encrypted ECPrivateKey structure.
732        ///
733        /// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`.
734        #[corresponds(PEM_read_bio_ECPrivateKey)]
735        private_key_from_pem_passphrase,
736
737        /// Deserializes a private key from a PEM-encoded encrypted ECPrivateKey structure.
738        ///
739        /// The callback should fill the password into the provided buffer and return its length.
740        ///
741        /// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`.
742        #[corresponds(PEM_read_bio_ECPrivateKey)]
743        private_key_from_pem_callback,
744        EcKey<Private>,
745        ffi::PEM_read_bio_ECPrivateKey
746    }
747
748    from_der! {
749        /// Decodes a DER-encoded elliptic curve private key structure.
750        #[corresponds(d2i_ECPrivateKey)]
751        private_key_from_der,
752        EcKey<Private>,
753        ffi::d2i_ECPrivateKey,
754        ::libc::c_long
755    }
756}
757
758impl<T> Clone for EcKey<T> {
759    fn clone(&self) -> EcKey<T> {
760        (**self).to_owned()
761    }
762}
763
764impl<T> fmt::Debug for EcKey<T> {
765    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
766        write!(f, "EcKey")
767    }
768}
769
770#[cfg(test)]
771mod test {
772    use hex::FromHex;
773
774    use super::*;
775    use crate::bn::{BigNum, BigNumContext};
776    use crate::nid::Nid;
777
778    #[test]
779    fn key_new_by_curve_name() {
780        EcKey::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
781    }
782
783    #[test]
784    fn generate() {
785        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
786        EcKey::generate(&group).unwrap();
787    }
788
789    #[test]
790    fn cofactor() {
791        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
792        let mut ctx = BigNumContext::new().unwrap();
793        let mut cofactor = BigNum::new().unwrap();
794        group.cofactor(&mut cofactor, &mut ctx).unwrap();
795        let one = BigNum::from_u32(1).unwrap();
796        assert_eq!(cofactor, one);
797    }
798
799    #[test]
800    #[allow(clippy::redundant_clone)]
801    fn dup() {
802        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
803        let key = EcKey::generate(&group).unwrap();
804        drop(key.clone());
805    }
806
807    #[test]
808    fn point_new() {
809        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
810        EcPoint::new(&group).unwrap();
811    }
812
813    #[test]
814    fn point_bytes() {
815        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
816        let key = EcKey::generate(&group).unwrap();
817        let point = key.public_key();
818        let mut ctx = BigNumContext::new().unwrap();
819        let bytes = point
820            .to_bytes(&group, PointConversionForm::COMPRESSED, &mut ctx)
821            .unwrap();
822        let point2 = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap();
823        assert!(point.eq(&group, &point2, &mut ctx).unwrap());
824    }
825
826    #[test]
827    fn point_owned() {
828        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
829        let key = EcKey::generate(&group).unwrap();
830        let point = key.public_key();
831        let owned = point.to_owned(&group).unwrap();
832        let mut ctx = BigNumContext::new().unwrap();
833        assert!(owned.eq(&group, point, &mut ctx).unwrap());
834    }
835
836    #[test]
837    fn mul_generator() {
838        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
839        let key = EcKey::generate(&group).unwrap();
840        let mut ctx = BigNumContext::new().unwrap();
841        let mut public_key = EcPoint::new(&group).unwrap();
842        public_key
843            .mul_generator(&group, key.private_key(), &ctx)
844            .unwrap();
845        assert!(public_key.eq(&group, key.public_key(), &mut ctx).unwrap());
846    }
847
848    #[test]
849    fn generator() {
850        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
851        let generator = group.generator();
852        let one = BigNum::from_u32(1).unwrap();
853        let mut ctx = BigNumContext::new().unwrap();
854        let mut ecp = EcPoint::new(&group).unwrap();
855        ecp.mul_generator(&group, &one, &ctx).unwrap();
856        assert!(ecp.eq(&group, generator, &mut ctx).unwrap());
857    }
858
859    #[test]
860    fn key_from_public_key() {
861        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
862        let key = EcKey::generate(&group).unwrap();
863        let mut ctx = BigNumContext::new().unwrap();
864        let bytes = key
865            .public_key()
866            .to_bytes(&group, PointConversionForm::COMPRESSED, &mut ctx)
867            .unwrap();
868
869        drop(key);
870        let public_key = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap();
871        let ec_key = EcKey::from_public_key(&group, &public_key).unwrap();
872        assert!(ec_key.check_key().is_ok());
873    }
874
875    #[test]
876    fn key_from_private_components() {
877        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
878        let key = EcKey::generate(&group).unwrap();
879
880        let dup_key =
881            EcKey::from_private_components(&group, key.private_key(), key.public_key()).unwrap();
882        dup_key.check_key().unwrap();
883
884        assert!(key.private_key() == dup_key.private_key());
885    }
886
887    #[test]
888    fn key_from_affine_coordinates() {
889        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
890        let x = Vec::from_hex("30a0424cd21c2944838a2d75c92b37e76ea20d9f00893a3b4eee8a3c0aafec3e")
891            .unwrap();
892        let y = Vec::from_hex("e04b65e92456d9888b52b379bdfbd51ee869ef1f0fc65b6659695b6cce081723")
893            .unwrap();
894
895        let xbn = BigNum::from_slice(&x).unwrap();
896        let ybn = BigNum::from_slice(&y).unwrap();
897
898        let ec_key = EcKey::from_public_key_affine_coordinates(&group, &xbn, &ybn).unwrap();
899        assert!(ec_key.check_key().is_ok());
900    }
901
902    #[test]
903    fn get_affine_coordinates() {
904        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
905        let x = Vec::from_hex("30a0424cd21c2944838a2d75c92b37e76ea20d9f00893a3b4eee8a3c0aafec3e")
906            .unwrap();
907        let y = Vec::from_hex("e04b65e92456d9888b52b379bdfbd51ee869ef1f0fc65b6659695b6cce081723")
908            .unwrap();
909
910        let xbn = BigNum::from_slice(&x).unwrap();
911        let ybn = BigNum::from_slice(&y).unwrap();
912
913        let ec_key = EcKey::from_public_key_affine_coordinates(&group, &xbn, &ybn).unwrap();
914
915        let mut xbn2 = BigNum::new().unwrap();
916        let mut ybn2 = BigNum::new().unwrap();
917        let mut ctx = BigNumContext::new().unwrap();
918        let ec_key_pk = ec_key.public_key();
919        ec_key_pk
920            .affine_coordinates_gfp(&group, &mut xbn2, &mut ybn2, &mut ctx)
921            .unwrap();
922        assert_eq!(xbn2, xbn);
923        assert_eq!(ybn2, ybn);
924    }
925}