use crate::error::{KeyRejected, Unspecified};
use native_ossl::digest::DigestAlg;
use native_ossl::params::ParamBuilder;
use native_ossl::pkey::{KeygenCtx, Pkey, Private, Public, SignInit, Signer, Verifier};
use std::ffi::CStr;
use crate::spki::{ED25519_SPKI_HEADER, P256_SPKI_HEADER, P384_SPKI_HEADER};
#[derive(Debug)]
pub struct Algorithm {
pub(crate) name: &'static str,
pub(crate) ec_curve: Option<&'static CStr>,
pub(crate) spki_header: Option<&'static [u8]>,
pub(crate) digest_name: Option<&'static CStr>,
pub(crate) rsa_pss: bool,
}
pub static ECDSA_P256_SHA256_ASN1: Algorithm = Algorithm {
name: "ECDSA_P256_SHA256_ASN1",
ec_curve: Some(c"P-256"),
spki_header: Some(P256_SPKI_HEADER),
digest_name: Some(c"SHA2-256"),
rsa_pss: false,
};
pub static ECDSA_P384_SHA384_ASN1: Algorithm = Algorithm {
name: "ECDSA_P384_SHA384_ASN1",
ec_curve: Some(c"P-384"),
spki_header: Some(P384_SPKI_HEADER),
digest_name: Some(c"SHA2-384"),
rsa_pss: false,
};
pub static ED25519: Algorithm = Algorithm {
name: "ED25519",
ec_curve: None,
spki_header: Some(ED25519_SPKI_HEADER),
digest_name: None,
rsa_pss: false,
};
pub static RSA_PKCS1_SHA256: Algorithm = Algorithm {
name: "RSA_PKCS1_SHA256",
ec_curve: None,
spki_header: None,
digest_name: Some(c"SHA2-256"),
rsa_pss: false,
};
pub static RSA_PKCS1_SHA384: Algorithm = Algorithm {
name: "RSA_PKCS1_SHA384",
ec_curve: None,
spki_header: None,
digest_name: Some(c"SHA2-384"),
rsa_pss: false,
};
pub static RSA_PKCS1_SHA512: Algorithm = Algorithm {
name: "RSA_PKCS1_SHA512",
ec_curve: None,
spki_header: None,
digest_name: Some(c"SHA2-512"),
rsa_pss: false,
};
pub static RSA_PSS_SHA256: Algorithm = Algorithm {
name: "RSA_PSS_SHA256",
ec_curve: None,
spki_header: None,
digest_name: Some(c"SHA2-256"),
rsa_pss: true,
};
pub static RSA_PSS_SHA384: Algorithm = Algorithm {
name: "RSA_PSS_SHA384",
ec_curve: None,
spki_header: None,
digest_name: Some(c"SHA2-384"),
rsa_pss: true,
};
pub static RSA_PSS_SHA512: Algorithm = Algorithm {
name: "RSA_PSS_SHA512",
ec_curve: None,
spki_header: None,
digest_name: Some(c"SHA2-512"),
rsa_pss: true,
};
#[derive(Debug)]
pub struct Signature(Vec<u8>);
impl AsRef<[u8]> for Signature {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
pub struct EcdsaKeyPair {
alg: &'static Algorithm,
priv_key: Pkey<Private>,
pub_key_bytes: Vec<u8>,
}
impl std::fmt::Debug for EcdsaKeyPair {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EcdsaKeyPair")
.field("alg", &self.alg.name)
.finish_non_exhaustive()
}
}
impl EcdsaKeyPair {
pub fn generate(alg: &'static Algorithm) -> Result<Self, Unspecified> {
let curve = alg.ec_curve.ok_or(Unspecified)?;
let spki_header = alg.spki_header.ok_or(Unspecified)?;
let mut ctx = KeygenCtx::new(c"EC").map_err(|_| Unspecified)?;
let params = ParamBuilder::new()
.and_then(|b| b.push_utf8_string(c"group", curve))
.and_then(ParamBuilder::build)
.map_err(|_| Unspecified)?;
ctx.set_params(¶ms).map_err(|_| Unspecified)?;
let priv_key = ctx.generate().map_err(|_| Unspecified)?;
let spki = priv_key.public_key_to_der().map_err(|_| Unspecified)?;
let raw_pub = spki.get(spki_header.len()..).ok_or(Unspecified)?.to_vec();
Ok(Self {
alg,
priv_key,
pub_key_bytes: raw_pub,
})
}
pub fn from_pkcs8(
alg: &'static Algorithm,
pkcs8: &[u8],
_rng: &dyn crate::rand::SecureRandom,
) -> Result<Self, KeyRejected> {
let spki_header = alg
.spki_header
.ok_or_else(|| KeyRejected::new("wrong algorithm kind for ECDSA"))?;
let priv_key =
Pkey::<Private>::from_der(pkcs8).map_err(|_| KeyRejected::new("bad PKCS8 DER"))?;
if !priv_key.is_a(c"EC") {
return Err(KeyRejected::new("not an EC key"));
}
let spki = priv_key
.public_key_to_der()
.map_err(|_| KeyRejected::new("public_key_to_der failed"))?;
if !spki.starts_with(spki_header) {
return Err(KeyRejected::new("wrong EC curve"));
}
let raw_pub = spki[spki_header.len()..].to_vec();
Ok(Self {
alg,
priv_key,
pub_key_bytes: raw_pub,
})
}
#[must_use]
pub fn public_key(&self) -> &[u8] {
&self.pub_key_bytes
}
pub fn sign(
&self,
_rng: &dyn crate::rand::SecureRandom,
message: &[u8],
) -> Result<Signature, Unspecified> {
let digest_name = self.alg.digest_name.ok_or(Unspecified)?;
let digest_alg = DigestAlg::fetch(digest_name, None).map_err(|_| Unspecified)?;
let init = SignInit {
digest: Some(&digest_alg),
params: None,
};
let mut signer = Signer::new(&self.priv_key, &init).map_err(|_| Unspecified)?;
let sig = signer.sign_oneshot(message).map_err(|_| Unspecified)?;
Ok(Signature(sig))
}
}
pub struct Ed25519KeyPair {
priv_key: Pkey<Private>,
pub_key_bytes: Vec<u8>,
}
impl std::fmt::Debug for Ed25519KeyPair {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ed25519KeyPair").finish_non_exhaustive()
}
}
impl Ed25519KeyPair {
pub fn generate() -> Result<Self, Unspecified> {
let mut ctx = KeygenCtx::new(c"ED25519").map_err(|_| Unspecified)?;
let priv_key = ctx.generate().map_err(|_| Unspecified)?;
let spki = priv_key.public_key_to_der().map_err(|_| Unspecified)?;
let raw_pub = spki
.get(ED25519_SPKI_HEADER.len()..)
.ok_or(Unspecified)?
.to_vec();
Ok(Self {
priv_key,
pub_key_bytes: raw_pub,
})
}
pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
let priv_key =
Pkey::<Private>::from_der(pkcs8).map_err(|_| KeyRejected::new("bad PKCS8 DER"))?;
if !priv_key.is_a(c"ED25519") {
return Err(KeyRejected::new("not an Ed25519 key"));
}
let spki = priv_key
.public_key_to_der()
.map_err(|_| KeyRejected::new("public_key_to_der failed"))?;
let raw_pub = spki
.get(ED25519_SPKI_HEADER.len()..)
.ok_or_else(|| KeyRejected::new("unexpected SPKI layout"))?
.to_vec();
Ok(Self {
priv_key,
pub_key_bytes: raw_pub,
})
}
#[must_use]
pub fn public_key(&self) -> &[u8] {
&self.pub_key_bytes
}
pub fn sign(&self, message: &[u8]) -> Result<Signature, Unspecified> {
let init = SignInit {
digest: None,
params: None,
};
let mut signer = Signer::new(&self.priv_key, &init).map_err(|_| Unspecified)?;
let sig = signer.sign_oneshot(message).map_err(|_| Unspecified)?;
Ok(Signature(sig))
}
}
pub struct UnparsedPublicKey<'a> {
alg: &'static Algorithm,
bytes: &'a [u8],
}
impl<'a> UnparsedPublicKey<'a> {
#[must_use]
pub fn new(algorithm: &'static Algorithm, bytes: &'a [u8]) -> Self {
Self {
alg: algorithm,
bytes,
}
}
}
pub fn verify(
algorithm: &'static Algorithm,
public_key: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<(), Unspecified> {
UnparsedPublicKey::new(algorithm, public_key).verify(message, signature)
}
impl UnparsedPublicKey<'_> {
pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), Unspecified> {
let pub_key = load_public_key(self.alg, self.bytes)?;
verify_with_key(self.alg, &pub_key, message, signature)
}
}
fn load_public_key(alg: &'static Algorithm, raw: &[u8]) -> Result<Pkey<Public>, Unspecified> {
let spki = match alg.spki_header {
Some(header) => {
let mut v = header.to_vec();
v.extend_from_slice(raw);
v
}
None => raw.to_vec(),
};
Pkey::<Public>::from_der(&spki).map_err(|_| Unspecified)
}
fn verify_with_key(
alg: &'static Algorithm,
pub_key: &Pkey<Public>,
message: &[u8],
signature: &[u8],
) -> Result<(), Unspecified> {
match alg.digest_name {
None => {
let init = SignInit {
digest: None,
params: None,
};
let mut verifier = Verifier::new(pub_key, &init).map_err(|_| Unspecified)?;
let ok = verifier
.verify_oneshot(message, signature)
.map_err(|_| Unspecified)?;
if ok {
Ok(())
} else {
Err(Unspecified)
}
}
Some(digest_name) => verify_digest(pub_key, digest_name, alg.rsa_pss, message, signature),
}
}
fn verify_digest(
pub_key: &Pkey<Public>,
digest_name: &'static CStr,
rsa_pss: bool,
message: &[u8],
signature: &[u8],
) -> Result<(), Unspecified> {
let digest_alg = DigestAlg::fetch(digest_name, None).map_err(|_| Unspecified)?;
let params = if rsa_pss {
let p = ParamBuilder::new()
.and_then(|b| b.push_utf8_string(c"pad-mode", c"pss"))
.and_then(ParamBuilder::build)
.map_err(|_| Unspecified)?;
Some(p)
} else {
None
};
let init = SignInit {
digest: Some(&digest_alg),
params: params.as_ref(),
};
let mut verifier = Verifier::new(pub_key, &init).map_err(|_| Unspecified)?;
let ok = verifier
.verify_oneshot(message, signature)
.map_err(|_| Unspecified)?;
if ok {
Ok(())
} else {
Err(Unspecified)
}
}