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.
45use std::borrow::Cow;
6use std::fmt::{self, Debug, Display, Formatter};
7use std::os::raw::c_int;
89use boringssl::{self, BoringError};
10use util::Sealed;
11use Error;
1213/// 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 {
19use Error;
2021use boringssl::{self, CRef};
22use util::Sealed;
2324/// An elliptic curve.
25 ///
26 /// `PCurve` is implemented by `P256`, `P384`, `P521`.
27pub 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.
32fn nid() -> i32;
3334/// Returns the group named by `Self::nid()`.
35fn group() -> CRef<'static, boringssl::EC_GROUP> {
36 CRef::ec_group_new_by_curve_name(Self::nid()).unwrap()
37 }
3839/// 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.
43fn validate_group(group: CRef<'_, boringssl::EC_GROUP>) -> Result<(), Error>;
44 }
45}
4647/// 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 {}
5960/// 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;
6970impl Display for P256 {
71fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
72write!(f, "P-256")
73 }
74}
75impl Display for P384 {
76fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
77write!(f, "P-384")
78 }
79}
80impl Display for P521 {
81fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
82write!(f, "P-521")
83 }
84}
8586const 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;
8990macro_rules! impl_curve {
91 ($name:ident, $str:expr, $nid:ident) => {
92impl self::inner::PCurve for $name {
93fn nid() -> i32 {
94$nid
95}
96fn validate_group(
97 group: boringssl::CRef<'_, boringssl::EC_GROUP>,
98 ) -> Result<(), ::Error> {
99let nid = group.ec_group_get_curve_name();
100if nid != $nid {
101return Err(::Error::new(format!(
102concat!("unexpected curve: got {}; want ", $str),
103 nid_name(nid).unwrap(),
104 )));
105 }
106Ok(())
107 }
108 }
109110impl Sealed for $name {}
111impl PCurve for $name {}
112 };
113}
114115impl_curve!(P256, "P-256", NID_P256);
116impl_curve!(P384, "P-384", NID_P384);
117impl_curve!(P521, "P-521", NID_P521);
118119/// A dynamic representation of a curve.
120pub enum CurveKind {
121 P256,
122 P384,
123 P521,
124}
125126impl CurveKind {
127/// Get the `CurveKind` associated with a NID.
128pub fn from_nid(nid: i32) -> Result<CurveKind, Error> {
129match nid {
130self::NID_P256 => Ok(CurveKind::P256),
131self::NID_P384 => Ok(CurveKind::P384),
132self::NID_P521 => Ok(CurveKind::P521),
133_ => Err(Error::new(format!("unsupported curve: {}", nid_name(nid).unwrap()))),
134 }
135 }
136}
137138// NOTE: Can only return an error due to an unknown NID
139fn nid_name(nid: c_int) -> Result<Cow<'static, str>, BoringError> {
140Ok(boringssl::ec_curve_nid2nist(nid)?.to_string_lossy())
141}