acme2-eab 0.5.7

A Tokio and OpenSSL based ACMEv2 client.
Documentation
use crate::error::*;
use crate::helpers::*;
use openssl::hash::MessageDigest;
use openssl::pkey::Id;
use openssl::pkey::PKey;
use openssl::pkey::Private;
use openssl::sign::Signer;
use serde::Deserialize;
use serde::Serialize;

#[derive(Serialize, Deserialize, Clone, Default)]
struct JwsHeader {
    #[serde(skip_serializing_if = "Option::is_none")]
    nonce: Option<String>,
    alg: String,
    url: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    kid: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    jwk: Option<Jwk>,
}

#[derive(Serialize, Deserialize, Clone, Default)]
pub(crate) struct Jwk {
    e: String,
    kty: String,
    n: String,
}

impl Jwk {
    pub fn new(pkey: &PKey<Private>) -> Jwk {
        Jwk {
            e: b64(&pkey.rsa().unwrap().e().to_vec()),
            kty: "RSA".to_string(),
            n: b64(&pkey.rsa().unwrap().n().to_vec()),
        }
    }
}

#[derive(Serialize, Debug, Clone)]
pub struct JwsResult {
    protected: String,
    payload: String,
    signature: String,
}

pub(crate) fn jws(
    url: &str,
    nonce: Option<String>,
    payload: &str,
    pkey: &PKey<Private>,
    account_id: Option<String>,
) -> Result<JwsResult, Error> {
    let payload_b64 = b64(payload.as_bytes());

    let alg: String = match pkey.id() {
        Id::RSA => "RS256".into(),
        Id::HMAC => "HS256".into(),
        _ => todo!(),
    };

    let mut header = JwsHeader {
        nonce,
        alg,
        url: url.to_string(),
        ..Default::default()
    };

    if let Some(kid) = account_id {
        header.kid = kid.into();
    } else {
        header.jwk = Some(Jwk::new(pkey));
    }

    let protected_b64 = b64(&serde_json::to_string(&header)?.into_bytes());

    let signature_b64 = {
        let mut signer = Signer::new(MessageDigest::sha256(), pkey)?;
        signer.update(&format!("{}.{}", protected_b64, payload_b64).into_bytes())?;
        b64(&signer.sign_to_vec()?)
    };

    Ok(JwsResult {
        protected: protected_b64,
        payload: payload_b64,
        signature: signature_b64,
    })
}