use std::{
fmt,
};
use crate::{Error, Result};
#[cfg(test)]
use quickcheck::{Arbitrary, Gen};
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum Curve {
NistP256,
NistP384,
NistP521,
BrainpoolP256,
BrainpoolP384,
BrainpoolP512,
Ed25519,
Cv25519,
Unknown(Box<[u8]>),
}
assert_send_and_sync!(Curve);
const CURVE_VARIANTS: [Curve; 8] = [
Curve::NistP256,
Curve::NistP384,
Curve::NistP521,
Curve::BrainpoolP256,
Curve::BrainpoolP384,
Curve::BrainpoolP512,
Curve::Ed25519,
Curve::Cv25519,
];
impl Curve {
pub fn bits(&self) -> Result<usize> {
use self::Curve::*;
match self {
NistP256 => Ok(256),
NistP384 => Ok(384),
NistP521 => Ok(521),
BrainpoolP256 => Ok(256),
BrainpoolP384 => Ok(384),
BrainpoolP512 => Ok(512),
Ed25519 => Ok(256),
Cv25519 => Ok(256),
Unknown(_) =>
Err(Error::UnsupportedEllipticCurve(self.clone()).into()),
}
}
pub fn field_size(&self) -> Result<usize> {
self.bits()
.map(|bits| (bits + 7) / 8)
}
}
impl fmt::Display for Curve {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Curve::*;
struct DotEncoded<'o>(&'o [u8]);
impl fmt::Display for DotEncoded<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut oid = self.0;
if oid.is_empty() {
write!(f, "[invalid]")?;
return Ok(());
}
let first = oid[0] / 40;
let second = oid[0] % 40;
oid = &oid[1..];
write!(f, "{}.{}", first, second)?;
let mut acc: usize = 0;
for b in oid {
if b & 0x80 > 0 {
acc *= 0x80;
acc += (b & 0x7f) as usize;
} else {
acc *= 0x80;
acc += (b & 0x7f) as usize;
write!(f, ".{}", acc)?;
acc = 0;
}
}
Ok(())
}
}
if f.alternate() {
match *self {
NistP256 => f.write_str("NIST curve P-256"),
NistP384 => f.write_str("NIST curve P-384"),
NistP521 => f.write_str("NIST curve P-521"),
BrainpoolP256 => f.write_str("brainpoolP256r1"),
BrainpoolP384 => f.write_str("brainpoolP384r1"),
BrainpoolP512 => f.write_str("brainpoolP512r1"),
Ed25519
=> f.write_str("D.J. Bernstein's \"Twisted\" Edwards curve Ed25519"),
Cv25519
=> f.write_str("Elliptic curve Diffie-Hellman using D.J. Bernstein's Curve25519"),
Unknown(ref oid)
=> write!(f, "Unknown curve (OID: {})", DotEncoded(oid)),
}
} else {
match *self {
NistP256 => f.write_str("NIST P-256"),
NistP384 => f.write_str("NIST P-384"),
NistP521 => f.write_str("NIST P-521"),
BrainpoolP256 => f.write_str("brainpoolP256r1"),
BrainpoolP384 => f.write_str("brainpoolP384r1"),
BrainpoolP512 => f.write_str("brainpoolP512r1"),
Ed25519
=> f.write_str("Ed25519"),
Cv25519
=> f.write_str("Curve25519"),
Unknown(ref oid)
=> write!(f, "Unknown curve {}", DotEncoded(oid)),
}
}
}
}
const NIST_P256_OID: &[u8] = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07];
const NIST_P384_OID: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x22];
const NIST_P521_OID: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x23];
const BRAINPOOL_P256_OID: &[u8] =
&[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07];
const BRAINPOOL_P384_OID: &[u8] =
&[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B];
const BRAINPOOL_P512_OID: &[u8] =
&[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D];
const ED25519_OID: &[u8] =
&[0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01];
const CV25519_OID: &[u8] =
&[0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01];
impl Curve {
pub fn from_oid(oid: &[u8]) -> Curve {
match oid {
NIST_P256_OID => Curve::NistP256,
NIST_P384_OID => Curve::NistP384,
NIST_P521_OID => Curve::NistP521,
BRAINPOOL_P256_OID => Curve::BrainpoolP256,
BRAINPOOL_P384_OID => Curve::BrainpoolP384,
BRAINPOOL_P512_OID => Curve::BrainpoolP512,
ED25519_OID => Curve::Ed25519,
CV25519_OID => Curve::Cv25519,
oid => Curve::Unknown(Vec::from(oid).into_boxed_slice()),
}
}
pub fn oid(&self) -> &[u8] {
match self {
Curve::NistP256 => NIST_P256_OID,
Curve::NistP384 => NIST_P384_OID,
Curve::NistP521 => NIST_P521_OID,
Curve::BrainpoolP256 => BRAINPOOL_P256_OID,
Curve::BrainpoolP384 => BRAINPOOL_P384_OID,
Curve::BrainpoolP512 => BRAINPOOL_P512_OID,
Curve::Ed25519 => ED25519_OID,
Curve::Cv25519 => CV25519_OID,
Curve::Unknown(ref oid) => oid,
}
}
pub fn is_supported(&self) -> bool {
use crate::crypto::backend::{Backend, interface::Asymmetric};
Backend::supports_curve(self)
}
pub fn variants() -> impl Iterator<Item=Self> {
CURVE_VARIANTS.iter().cloned()
}
}
#[cfg(test)]
impl Arbitrary for Curve {
fn arbitrary(g: &mut Gen) -> Self {
match u8::arbitrary(g) % 9 {
0 => Curve::NistP256,
1 => Curve::NistP384,
2 => Curve::NistP521,
3 => Curve::BrainpoolP256,
4 => Curve::BrainpoolP384,
5 => Curve::BrainpoolP512,
6 => Curve::Ed25519,
7 => Curve::Cv25519,
8 => Curve::Unknown({
let mut k = <Vec<u8>>::arbitrary(g);
k.truncate(255);
k.into_boxed_slice()
}),
_ => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
quickcheck! {
fn curve_roundtrip(curve: Curve) -> bool {
curve == Curve::from_oid(curve.oid())
}
}
}