use std::{convert::TryFrom, fmt};
use once_cell::sync::Lazy;
use openssl::{
ec::{EcGroup, EcGroupRef},
nid::Nid,
};
use serde::{Deserialize, Serialize};
use crate::{error, jws};
#[cfg(feature = "private-keys")]
mod private;
mod public;
#[cfg(feature = "private-keys")]
#[cfg_attr(docsrs, doc(cfg(feature = "private-keys")))]
pub use private::PrivateKey;
pub use public::PublicKey;
static P256: Lazy<EcGroup> = Lazy::new(|| EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap());
static P384: Lazy<EcGroup> = Lazy::new(|| EcGroup::from_curve_name(Nid::SECP384R1).unwrap());
static P521: Lazy<EcGroup> = Lazy::new(|| EcGroup::from_curve_name(Nid::SECP521R1).unwrap());
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
pub enum Curve {
#[serde(rename = "P-256")]
P256,
#[serde(rename = "P-384")]
P384,
#[serde(rename = "P-521")]
P521,
}
impl Curve {
fn to_group(self) -> &'static EcGroupRef {
match self {
Curve::P256 => &P256,
Curve::P384 => &P384,
Curve::P521 => &P521,
}
}
#[cfg(feature = "private-keys")]
fn from_group(group: &EcGroupRef) -> Option<Self> {
let nid = group.curve_name()?;
if nid == P256.curve_name().unwrap() {
Some(Curve::P256)
} else if nid == P384.curve_name().unwrap() {
Some(Curve::P384)
} else if nid == P521.curve_name().unwrap() {
Some(Curve::P521)
} else {
None
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
#[must_use]
pub struct EllipticCurve {
#[cfg(feature = "private-keys")]
key: MaybePrivate,
#[cfg(not(feature = "private-keys"))]
key: PublicKey,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
#[cfg(feature = "private-keys")]
enum MaybePrivate {
PublicAndPrivate(PrivateKey),
PublicOnly(PublicKey),
}
impl EllipticCurve {
#[cfg(feature = "private-keys")]
pub fn generate(curve: Curve) -> Result<Self, error::Unexpected> {
let private_key = PrivateKey::generate(curve)?;
Ok(Self::from(private_key))
}
#[cfg(feature = "private-keys")]
pub(crate) fn private_key(&self) -> Option<&PrivateKey> {
match &self.key {
MaybePrivate::PublicAndPrivate(p) => Some(p),
MaybePrivate::PublicOnly(_) => None,
}
}
#[cfg(feature = "private-keys")]
pub(crate) fn public_key(&self) -> &PublicKey {
match &self.key {
MaybePrivate::PublicAndPrivate(p) => p.public_key(),
MaybePrivate::PublicOnly(p) => p,
}
}
#[cfg(not(feature = "private-keys"))]
pub(crate) fn public_key(&self) -> &PublicKey {
&self.key
}
#[cfg(feature = "private-keys")]
pub fn public_only(self) -> Self {
match self.key {
MaybePrivate::PublicAndPrivate(p) => Self::from(p.into_public_key()),
_ => self,
}
}
#[cfg(not(feature = "private-keys"))]
pub fn public_only(self) -> Self {
self
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
#[allow(clippy::upper_case_acronyms)]
#[non_exhaustive]
pub enum SigningAlgorithm {
ES256,
ES384,
ES512,
}
impl From<SigningAlgorithm> for jws::Algorithm {
fn from(alg: SigningAlgorithm) -> Self {
Self::EllipticCurve(alg)
}
}
impl TryFrom<jws::Algorithm> for SigningAlgorithm {
type Error = error::IncompatibleAlgorithm;
fn try_from(alg: jws::Algorithm) -> Result<Self, Self::Error> {
match alg {
jws::Algorithm::EllipticCurve(alg) => Ok(alg),
#[allow(unreachable_patterns)]
_ => Err(error::incompatible_algorithm(alg)),
}
}
}
impl SigningAlgorithm {
fn verification_algorithm(self) -> &'static ring::signature::EcdsaVerificationAlgorithm {
match self {
Self::ES256 => &ring::signature::ECDSA_P256_SHA256_FIXED,
Self::ES384 => &ring::signature::ECDSA_P384_SHA384_FIXED,
Self::ES512 => unimplemented!(),
}
}
#[cfg(feature = "private-keys")]
fn signing_algorithm(self) -> &'static ring::signature::EcdsaSigningAlgorithm {
match self {
Self::ES256 => &ring::signature::ECDSA_P256_SHA256_FIXED_SIGNING,
Self::ES384 => &ring::signature::ECDSA_P384_SHA384_FIXED_SIGNING,
Self::ES512 => unimplemented!(),
}
}
pub fn signature_size(self) -> usize {
match self {
Self::ES256 => 64,
Self::ES384 => 96,
Self::ES512 => 131,
}
}
}
impl From<SigningAlgorithm> for Curve {
fn from(alg: SigningAlgorithm) -> Self {
match alg {
SigningAlgorithm::ES256 => Self::P256,
SigningAlgorithm::ES384 => Self::P384,
SigningAlgorithm::ES512 => Self::P521,
}
}
}
impl From<Curve> for SigningAlgorithm {
fn from(crv: Curve) -> Self {
match crv {
Curve::P256 => Self::ES256,
Curve::P384 => Self::ES384,
Curve::P521 => Self::ES512,
}
}
}
impl jws::Verifier for EllipticCurve {
type Algorithm = SigningAlgorithm;
type Error = error::SignatureMismatch;
fn can_verify(&self, alg: Self::Algorithm) -> bool {
self.public_key().can_verify(alg)
}
fn verify(
&self,
alg: Self::Algorithm,
data: &[u8],
signature: &[u8],
) -> Result<(), Self::Error> {
self.public_key().verify(alg, data, signature)
}
}
impl jws::Signer for EllipticCurve {
type Algorithm = SigningAlgorithm;
type Error = error::SigningError;
#[cfg(feature = "private-keys")]
fn can_sign(&self, alg: Self::Algorithm) -> bool {
if let Some(p) = self.private_key() {
p.can_sign(alg)
} else {
false
}
}
#[cfg(not(feature = "private-keys"))]
fn can_sign(&self, _alg: Self::Algorithm) -> bool {
false
}
#[cfg(feature = "private-keys")]
fn sign(&self, alg: Self::Algorithm, data: &[u8]) -> Result<Vec<u8>, Self::Error> {
if let Some(p) = self.private_key() {
Ok(p.sign(alg, data)?)
} else {
Err(error::missing_private_key().into())
}
}
#[cfg(not(feature = "private-keys"))]
fn sign(&self, _alg: Self::Algorithm, _data: &[u8]) -> Result<Vec<u8>, Self::Error> {
Err(error::missing_private_key().into())
}
}
impl fmt::Display for SigningAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
Self::ES256 => "ES256",
Self::ES384 => "ES384",
Self::ES512 => "ES512",
};
f.write_str(s)
}
}
impl From<PublicKey> for EllipticCurve {
#[cfg(feature = "private-keys")]
fn from(key: PublicKey) -> Self {
Self {
key: MaybePrivate::PublicOnly(key),
}
}
#[cfg(not(feature = "private-keys"))]
fn from(key: PublicKey) -> Self {
Self { key }
}
}
#[cfg(feature = "private-keys")]
#[cfg_attr(docsrs, doc(cfg(feature = "private-keys")))]
impl From<PrivateKey> for EllipticCurve {
fn from(key: PrivateKey) -> Self {
Self {
key: MaybePrivate::PublicAndPrivate(key),
}
}
}