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}