#![deny(unsafe_code)]
#![deny(missing_docs)]
use std::fmt;
pub mod cert;
mod ecdsa;
pub mod jwk;
use crate::ecdsa::EcdsaSigningKey;
pub use crate::ecdsa::EcdsaAlgorithm;
pub struct Signature(Vec<u8>);
impl fmt::Debug for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Signature").field(&self.0).finish()
}
}
impl AsRef<[u8]> for Signature {
fn as_ref(&self) -> &[u8] {
self.0.as_slice()
}
}
impl From<Vec<u8>> for Signature {
fn from(value: Vec<u8>) -> Self {
Signature(value)
}
}
pub struct SigningError;
pub struct PublicKey(Box<dyn PublicKeyAlgorithm>);
impl PublicKey {
pub fn to_jwk(&self) -> crate::jwk::Jwk {
self.0.as_jwk()
}
pub fn algorithm(&self) -> pkcs8::AlgorithmIdentifier {
self.0.algorithm()
}
pub fn as_bytes(&self) -> Vec<u8> {
self.0.as_bytes()
}
}
impl PartialEq for PublicKey {
fn eq(&self, other: &Self) -> bool {
self.to_jwk() == other.to_jwk()
}
}
impl Eq for PublicKey {}
impl<T> From<Box<T>> for PublicKey
where
T: PublicKeyAlgorithm + 'static,
{
fn from(value: Box<T>) -> Self {
PublicKey(value as _)
}
}
impl fmt::Debug for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PublicKey").finish()
}
}
impl AsRef<[u8]> for PublicKey {
fn as_ref(&self) -> &[u8] {
todo!("PublicKey to bytes")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SignatureKind {
Ecdsa(EcdsaAlgorithm),
}
impl SignatureKind {
pub fn random(&self) -> SigningKey {
match self {
SignatureKind::Ecdsa(ecdsa) => SigningKey(ecdsa.random().into()),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct SigningKey(InnerSigningKey);
impl SigningKey {
pub fn from_pkcs8_pem(data: &str, signature: SignatureKind) -> Result<Self, pkcs8::Error> {
match signature {
SignatureKind::Ecdsa(algorithm) => Ok(SigningKey(InnerSigningKey::Ecdsa(Box::new(
EcdsaSigningKey::from_pkcs8_pem(data, algorithm)?,
)))),
}
}
pub fn kind(&self) -> SignatureKind {
match &self.0 {
InnerSigningKey::Ecdsa(key) => key.kind(),
}
}
}
impl SigningKey {
pub fn public_key(&self) -> PublicKey {
self.0.public_key()
}
pub fn as_jwk(&self) -> crate::jwk::Jwk {
self.0.as_jwk()
}
pub fn algorithm(&self) -> pkcs8::AlgorithmIdentifier {
self.0.algorithm()
}
}
impl pkcs8::EncodePrivateKey for SigningKey {
fn to_pkcs8_der(&self) -> pkcs8::Result<der::SecretDocument> {
self.0.to_pkcs8_der()
}
}
impl signature::Signer<Signature> for SigningKey {
fn try_sign(&self, msg: &[u8]) -> Result<Signature, ::ecdsa::Error> {
self.0.try_sign(msg)
}
}
impl signature::DigestSigner<sha2::Sha256, Signature> for SigningKey {
fn try_sign_digest(&self, digest: sha2::Sha256) -> Result<Signature, ::ecdsa::Error> {
self.0.try_sign_digest(digest)
}
}
pub(crate) enum InnerSigningKey {
Ecdsa(Box<dyn SigningKeyAlgorithm>),
}
impl InnerSigningKey {
pub(crate) fn as_jwk(&self) -> crate::jwk::Jwk {
match self {
InnerSigningKey::Ecdsa(ecdsa) => ecdsa.as_jwk(),
}
}
pub(crate) fn public_key(&self) -> PublicKey {
match self {
InnerSigningKey::Ecdsa(ecdsa) => ecdsa.public_key(),
}
}
pub(crate) fn algorithm(&self) -> pkcs8::AlgorithmIdentifier {
match self {
InnerSigningKey::Ecdsa(ecdsa) => ecdsa.algorithm(),
}
}
}
impl From<EcdsaSigningKey> for InnerSigningKey {
fn from(value: EcdsaSigningKey) -> Self {
InnerSigningKey::Ecdsa(Box::new(value) as _)
}
}
impl PartialEq for InnerSigningKey {
fn eq(&self, other: &Self) -> bool {
self.public_key() == other.public_key()
}
}
impl Eq for InnerSigningKey {}
impl pkcs8::EncodePrivateKey for InnerSigningKey {
fn to_pkcs8_der(&self) -> pkcs8::Result<der::SecretDocument> {
match self {
InnerSigningKey::Ecdsa(key) => key.to_pkcs8_der(),
}
}
}
impl signature::Signer<Signature> for InnerSigningKey {
fn try_sign(&self, msg: &[u8]) -> Result<Signature, ::ecdsa::Error> {
match self {
InnerSigningKey::Ecdsa(key) => key.try_sign(msg),
}
}
}
impl signature::DigestSigner<sha2::Sha256, Signature> for InnerSigningKey {
fn try_sign_digest(&self, digest: sha2::Sha256) -> Result<Signature, ::ecdsa::Error> {
match self {
InnerSigningKey::Ecdsa(key) => key.try_sign_digest(digest),
}
}
}
impl fmt::Debug for InnerSigningKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InnerSigningKey::Ecdsa(_) => f.write_str("ECDSA-Key"),
}
}
}
pub(crate) trait SigningKeyAlgorithm:
signature::Signer<Signature> + pkcs8::EncodePrivateKey
{
fn as_jwk(&self) -> crate::jwk::Jwk;
fn public_key(&self) -> PublicKey;
fn try_sign_digest(&self, digest: sha2::Sha256) -> Result<Signature, ::ecdsa::Error>;
fn algorithm(&self) -> pkcs8::AlgorithmIdentifier;
fn kind(&self) -> SignatureKind;
}
pub(crate) trait PublicKeyAlgorithm {
fn as_jwk(&self) -> crate::jwk::Jwk;
fn algorithm(&self) -> pkcs8::AlgorithmIdentifier;
fn as_bytes(&self) -> Vec<u8>;
}
#[cfg(test)]
mod test {
use std::sync::Arc;
use base64ct::LineEnding;
use pkcs8::EncodePrivateKey;
#[macro_export]
macro_rules! key {
($name:tt) => {
$crate::test::key(include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../reference-keys/",
$name,
".pem"
)))
};
}
pub(crate) fn key(private: &str) -> Arc<crate::SigningKey> {
let key = crate::SigningKey::from_pkcs8_pem(
private,
crate::SignatureKind::Ecdsa(crate::EcdsaAlgorithm::P256),
)
.unwrap();
Arc::new(key)
}
#[test]
fn roundtrip_key_through_pkcs8() {
let key = key!("ec-p255");
let pkcs8 = key.to_pkcs8_pem(LineEnding::default()).unwrap();
let key2 = crate::SigningKey::from_pkcs8_pem(&pkcs8, key.kind()).unwrap();
assert_eq!(key.as_ref(), &key2);
}
}