mundane/public/ec/
curve.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::borrow::Cow;
6use std::fmt::{self, Debug, Display, Formatter};
7use std::os::raw::c_int;
8
9use boringssl::{self, BoringError};
10use util::Sealed;
11use Error;
12
13/// The meat of the `Curve` trait.
14///
15/// We put the meat of the trait - an inner `Curve` trait which actually has
16/// methods on it - in a separate, private module because we don't want these
17/// methods to be visible to users.
18mod inner {
19    use Error;
20
21    use boringssl::{self, CRef};
22    use util::Sealed;
23
24    /// An elliptic curve.
25    ///
26    /// `PCurve` is implemented by `P256`, `P384`, `P521`.
27    pub trait PCurve: Sized + Sealed {
28        /// Returns this curve's NID.
29        ///
30        /// Callers are allowed to assume that this NID is a valid one, and are
31        /// allowed to panic if it is not.
32        fn nid() -> i32;
33
34        /// Returns the group named by `Self::nid()`.
35        fn group() -> CRef<'static, boringssl::EC_GROUP> {
36            CRef::ec_group_new_by_curve_name(Self::nid()).unwrap()
37        }
38
39        /// Validate that an `EC_GROUP` is matches this group.
40        ///
41        /// If `group` is not equal to the curve's group, `from_group` returns
42        /// an error.
43        fn validate_group(group: CRef<'_, boringssl::EC_GROUP>) -> Result<(), Error>;
44    }
45}
46
47/// A NIST P elliptic curve.
48///
49/// `PCurve` is implemented by [`P256`], [`P384`], and [`P521`]. The P-224 curve
50/// is considered insecure, and thus is not supported.
51///
52/// The P curves are defined by NIST and are used in the ECDSA and ECDH
53/// algorithms.
54///
55/// [`P256`]: ::public::ec::P256
56/// [`P384`]: ::public::ec::P384
57/// [`P521`]: ::public::ec::P521
58pub trait PCurve: Sized + Copy + Clone + Default + Display + Debug + self::inner::PCurve {}
59
60/// The P-256 curve.
61#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash)]
62pub struct P256;
63/// The P-384 curve.
64#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash)]
65pub struct P384;
66/// The P-521 curve.
67#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash)]
68pub struct P521;
69
70impl Display for P256 {
71    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
72        write!(f, "P-256")
73    }
74}
75impl Display for P384 {
76    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
77        write!(f, "P-384")
78    }
79}
80impl Display for P521 {
81    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
82        write!(f, "P-521")
83    }
84}
85
86const NID_P256: i32 = boringssl::NID_X9_62_prime256v1 as i32;
87const NID_P384: i32 = boringssl::NID_secp384r1 as i32;
88const NID_P521: i32 = boringssl::NID_secp521r1 as i32;
89
90macro_rules! impl_curve {
91    ($name:ident, $str:expr, $nid:ident) => {
92        impl self::inner::PCurve for $name {
93            fn nid() -> i32 {
94                $nid
95            }
96            fn validate_group(
97                group: boringssl::CRef<'_, boringssl::EC_GROUP>,
98            ) -> Result<(), ::Error> {
99                let nid = group.ec_group_get_curve_name();
100                if nid != $nid {
101                    return Err(::Error::new(format!(
102                        concat!("unexpected curve: got {}; want ", $str),
103                        nid_name(nid).unwrap(),
104                    )));
105                }
106                Ok(())
107            }
108        }
109
110        impl Sealed for $name {}
111        impl PCurve for $name {}
112    };
113}
114
115impl_curve!(P256, "P-256", NID_P256);
116impl_curve!(P384, "P-384", NID_P384);
117impl_curve!(P521, "P-521", NID_P521);
118
119/// A dynamic representation of a curve.
120pub enum CurveKind {
121    P256,
122    P384,
123    P521,
124}
125
126impl CurveKind {
127    /// Get the `CurveKind` associated with a NID.
128    pub fn from_nid(nid: i32) -> Result<CurveKind, Error> {
129        match nid {
130            self::NID_P256 => Ok(CurveKind::P256),
131            self::NID_P384 => Ok(CurveKind::P384),
132            self::NID_P521 => Ok(CurveKind::P521),
133            _ => Err(Error::new(format!("unsupported curve: {}", nid_name(nid).unwrap()))),
134        }
135    }
136}
137
138// NOTE: Can only return an error due to an unknown NID
139fn nid_name(nid: c_int) -> Result<Cow<'static, str>, BoringError> {
140    Ok(boringssl::ec_curve_nid2nist(nid)?.to_string_lossy())
141}