sequoia_openpgp/crypto/types/
curve.rs

1use std::{
2    fmt,
3};
4
5use crate::{Error, Result};
6
7#[cfg(test)]
8use quickcheck::{Arbitrary, Gen};
9
10/// Elliptic curves used in OpenPGP.
11///
12/// `PublicKeyAlgorithm` does not differentiate between elliptic
13/// curves.  Instead, the curve is specified using an OID prepended to
14/// the key material.  We provide this type to be able to match on the
15/// curves.
16#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
17#[non_exhaustive]
18pub enum Curve {
19    /// NIST curve P-256.
20    NistP256,
21    /// NIST curve P-384.
22    NistP384,
23    /// NIST curve P-521.
24    NistP521,
25    /// brainpoolP256r1.
26    BrainpoolP256,
27    /// brainpoolP384r1.
28    BrainpoolP384,
29    /// brainpoolP512r1.
30    BrainpoolP512,
31    /// D.J. Bernstein's "Twisted" Edwards curve Ed25519.
32    Ed25519,
33    /// Elliptic curve Diffie-Hellman using D.J. Bernstein's Curve25519.
34    Cv25519,
35    /// Unknown curve.
36    Unknown(Box<[u8]>),
37}
38
39assert_send_and_sync!(Curve);
40
41const CURVE_VARIANTS: [Curve; 8] = [
42    Curve::NistP256,
43    Curve::NistP384,
44    Curve::NistP521,
45    Curve::BrainpoolP256,
46    Curve::BrainpoolP384,
47    Curve::BrainpoolP512,
48    Curve::Ed25519,
49    Curve::Cv25519,
50];
51
52impl Curve {
53    /// Returns the length of public keys over this curve in bits.
54    ///
55    /// For the Kobliz curves this is the size of the underlying
56    /// finite field.  For X25519 it is 256.
57    ///
58    /// This value is also equal to the length of a coordinate in bits.
59    ///
60    /// Note: This information is useless and should not be used to
61    /// gauge the security of a particular curve. This function exists
62    /// only because some legacy PGP application like HKP need it.
63    ///
64    /// # Examples
65    ///
66    /// ```rust
67    /// # fn main() -> sequoia_openpgp::Result<()> {
68    /// use sequoia_openpgp as openpgp;
69    /// use openpgp::types::Curve;
70    ///
71    /// assert_eq!(Curve::NistP256.bits()?, 256);
72    /// assert_eq!(Curve::NistP384.bits()?, 384);
73    /// assert_eq!(Curve::Ed25519.bits()?, 256);
74    /// assert!(Curve::Unknown(Box::new([0x2B, 0x11])).bits().is_err());
75    /// # Ok(()) }
76    /// ```
77    pub fn bits(&self) -> Result<usize> {
78        use self::Curve::*;
79
80        match self {
81            NistP256 => Ok(256),
82            NistP384 => Ok(384),
83            NistP521 => Ok(521),
84            BrainpoolP256 => Ok(256),
85            BrainpoolP384 => Ok(384),
86            BrainpoolP512 => Ok(512),
87            Ed25519 => Ok(256),
88            Cv25519 => Ok(256),
89            Unknown(_) =>
90                Err(Error::UnsupportedEllipticCurve(self.clone()).into()),
91        }
92    }
93
94    /// Returns the curve's field size in bytes.
95    ///
96    /// # Examples
97    ///
98    /// ```rust
99    /// # fn main() -> sequoia_openpgp::Result<()> {
100    /// use sequoia_openpgp as openpgp;
101    /// use openpgp::types::Curve;
102    ///
103    /// assert_eq!(Curve::NistP256.field_size()?, 32);
104    /// assert_eq!(Curve::NistP384.field_size()?, 48);
105    /// assert_eq!(Curve::NistP521.field_size()?, 66);
106    /// assert_eq!(Curve::Ed25519.field_size()?, 32);
107    /// assert!(Curve::Unknown(Box::new([0x2B, 0x11])).field_size().is_err());
108    /// # Ok(()) }
109    /// ```
110    pub fn field_size(&self) -> Result<usize> {
111        self.bits()
112            .map(|bits| (bits + 7) / 8)
113    }
114}
115
116/// Formats the elliptic curve name.
117///
118/// There are two ways the elliptic curve name can be formatted.  By
119/// default the short name is used.  The alternate format uses the
120/// full curve name.
121///
122/// # Examples
123///
124/// ```
125/// use sequoia_openpgp as openpgp;
126/// use openpgp::types::Curve;
127///
128/// // default, short format
129/// assert_eq!("NIST P-256", format!("{}", Curve::NistP256));
130///
131/// // alternate, long format
132/// assert_eq!("NIST curve P-256", format!("{:#}", Curve::NistP256));
133/// ```
134impl fmt::Display for Curve {
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        use self::Curve::*;
137
138        struct DotEncoded<'o>(&'o [u8]);
139        impl fmt::Display for DotEncoded<'_> {
140            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141                let mut oid = self.0;
142                if oid.is_empty() {
143                    write!(f, "[invalid]")?;
144                    return Ok(());
145                }
146
147                // The first octet encodes two values.
148                let first = oid[0] / 40;
149                let second = oid[0] % 40;
150                oid = &oid[1..];
151                write!(f, "{}.{}", first, second)?;
152
153                let mut acc: usize = 0;
154                for b in oid {
155                    if b & 0x80 > 0 {
156                        acc *= 0x80;
157                        acc += (b & 0x7f) as usize;
158                    } else {
159                        acc *= 0x80;
160                        acc += (b & 0x7f) as usize;
161                        write!(f, ".{}", acc)?;
162                        acc = 0;
163                    }
164                }
165
166                Ok(())
167            }
168        }
169
170
171        if f.alternate() {
172            match *self {
173                NistP256 => f.write_str("NIST curve P-256"),
174                NistP384 => f.write_str("NIST curve P-384"),
175                NistP521 => f.write_str("NIST curve P-521"),
176                BrainpoolP256 => f.write_str("brainpoolP256r1"),
177                BrainpoolP384 => f.write_str("brainpoolP384r1"),
178                BrainpoolP512 => f.write_str("brainpoolP512r1"),
179                Ed25519
180                    => f.write_str("D.J. Bernstein's \"Twisted\" Edwards curve Ed25519"),
181                Cv25519
182                    => f.write_str("Elliptic curve Diffie-Hellman using D.J. Bernstein's Curve25519"),
183                Unknown(ref oid)
184                    => write!(f, "Unknown curve (OID: {})", DotEncoded(oid)),
185            }
186        } else {
187            match *self {
188                NistP256 => f.write_str("NIST P-256"),
189                NistP384 => f.write_str("NIST P-384"),
190                NistP521 => f.write_str("NIST P-521"),
191                BrainpoolP256 => f.write_str("brainpoolP256r1"),
192                BrainpoolP384 => f.write_str("brainpoolP384r1"),
193                BrainpoolP512 => f.write_str("brainpoolP512r1"),
194                Ed25519
195                    => f.write_str("Ed25519"),
196                Cv25519
197                    => f.write_str("Curve25519"),
198                Unknown(ref oid)
199                    => write!(f, "Unknown curve {}", DotEncoded(oid)),
200            }
201        }
202    }
203}
204
205const NIST_P256_OID: &[u8] = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07];
206const NIST_P384_OID: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x22];
207const NIST_P521_OID: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x23];
208const BRAINPOOL_P256_OID: &[u8] =
209    &[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07];
210const BRAINPOOL_P384_OID: &[u8] =
211    &[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B];
212const BRAINPOOL_P512_OID: &[u8] =
213    &[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D];
214const ED25519_OID: &[u8] =
215    &[0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01];
216const CV25519_OID: &[u8] =
217    &[0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01];
218
219impl Curve {
220    /// Parses the given OID.
221    ///
222    /// # Examples
223    ///
224    /// ```rust
225    /// use sequoia_openpgp as openpgp;
226    /// use openpgp::types::Curve;
227    ///
228    /// assert_eq!(Curve::from_oid(&[0x2B, 0x81, 0x04, 0x00, 0x22]), Curve::NistP384);
229    /// assert_eq!(Curve::from_oid(&[0x2B, 0x11]), Curve::Unknown(Box::new([0x2B, 0x11])));
230    /// ```
231    pub fn from_oid(oid: &[u8]) -> Curve {
232        // Match on OIDs, see section 11 of RFC6637.
233        match oid {
234            NIST_P256_OID => Curve::NistP256,
235            NIST_P384_OID => Curve::NistP384,
236            NIST_P521_OID => Curve::NistP521,
237            BRAINPOOL_P256_OID => Curve::BrainpoolP256,
238            BRAINPOOL_P384_OID => Curve::BrainpoolP384,
239            BRAINPOOL_P512_OID => Curve::BrainpoolP512,
240            ED25519_OID => Curve::Ed25519,
241            CV25519_OID => Curve::Cv25519,
242            oid => Curve::Unknown(Vec::from(oid).into_boxed_slice()),
243        }
244    }
245
246    /// Returns this curve's OID.
247    ///
248    /// # Examples
249    ///
250    /// ```rust
251    /// use sequoia_openpgp as openpgp;
252    /// use openpgp::types::Curve;
253    ///
254    /// assert_eq!(Curve::NistP384.oid(), &[0x2B, 0x81, 0x04, 0x00, 0x22]);
255    /// assert_eq!(Curve::Unknown(Box::new([0x2B, 0x11])).oid(), &[0x2B, 0x11]);
256    /// ```
257    pub fn oid(&self) -> &[u8] {
258        match self {
259            Curve::NistP256 => NIST_P256_OID,
260            Curve::NistP384 => NIST_P384_OID,
261            Curve::NistP521 => NIST_P521_OID,
262            Curve::BrainpoolP256 => BRAINPOOL_P256_OID,
263            Curve::BrainpoolP384 => BRAINPOOL_P384_OID,
264            Curve::BrainpoolP512 => BRAINPOOL_P512_OID,
265            Curve::Ed25519 => ED25519_OID,
266            Curve::Cv25519 => CV25519_OID,
267            Curve::Unknown(ref oid) => oid,
268        }
269    }
270
271    /// Returns whether this algorithm is supported.
272    ///
273    /// # Examples
274    ///
275    /// ```rust
276    /// use sequoia_openpgp as openpgp;
277    /// use openpgp::types::Curve;
278    ///
279    /// assert!(Curve::Ed25519.is_supported());
280    /// assert!(!Curve::Unknown(Box::new([0x2B, 0x11])).is_supported());
281    /// ```
282    pub fn is_supported(&self) -> bool {
283        use crate::crypto::backend::{Backend, interface::Asymmetric};
284        Backend::supports_curve(self)
285    }
286
287    /// Returns an iterator over all valid variants.
288    pub fn variants() -> impl Iterator<Item=Self> {
289        CURVE_VARIANTS.iter().cloned()
290    }
291}
292
293#[cfg(test)]
294impl Arbitrary for Curve {
295    fn arbitrary(g: &mut Gen) -> Self {
296        match u8::arbitrary(g) % 9 {
297            0 => Curve::NistP256,
298            1 => Curve::NistP384,
299            2 => Curve::NistP521,
300            3 => Curve::BrainpoolP256,
301            4 => Curve::BrainpoolP384,
302            5 => Curve::BrainpoolP512,
303            6 => Curve::Ed25519,
304            7 => Curve::Cv25519,
305            8 => Curve::Unknown({
306                let mut k = <Vec<u8>>::arbitrary(g);
307                k.truncate(255);
308                k.into_boxed_slice()
309            }),
310            _ => unreachable!(),
311        }
312    }
313}
314
315#[cfg(test)]
316mod tests {
317    use super::*;
318
319    quickcheck! {
320        fn curve_roundtrip(curve: Curve) -> bool {
321            curve == Curve::from_oid(curve.oid())
322        }
323    }
324}