diff --git a/acme_common/src/crypto/jws_signature_algorithm.rs b/acme_common/src/crypto/jws_signature_algorithm.rs index 8a8d27d..b06beda 100644 --- a/acme_common/src/crypto/jws_signature_algorithm.rs +++ b/acme_common/src/crypto/jws_signature_algorithm.rs @@ -4,6 +4,9 @@ use std::str::FromStr; #[derive(Clone, Copy, Debug, PartialEq)] pub enum JwsSignatureAlgorithm { + Hs256, + Hs384, + Hs512, Rs256, Es256, Es384, @@ -18,6 +21,9 @@ impl FromStr for JwsSignatureAlgorithm { fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { + "hs256" => Ok(JwsSignatureAlgorithm::Hs256), + "hs384" => Ok(JwsSignatureAlgorithm::Hs384), + "hs512" => Ok(JwsSignatureAlgorithm::Hs512), "rs256" => Ok(JwsSignatureAlgorithm::Rs256), "es256" => Ok(JwsSignatureAlgorithm::Es256), "es384" => Ok(JwsSignatureAlgorithm::Es384), @@ -33,6 +39,9 @@ impl FromStr for JwsSignatureAlgorithm { impl fmt::Display for JwsSignatureAlgorithm { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match self { + JwsSignatureAlgorithm::Hs256 => "HS256", + JwsSignatureAlgorithm::Hs384 => "HS384", + JwsSignatureAlgorithm::Hs512 => "HS512", JwsSignatureAlgorithm::Rs256 => "RS256", JwsSignatureAlgorithm::Es256 => "ES256", JwsSignatureAlgorithm::Es384 => "ES384", diff --git a/acme_common/src/crypto/openssl_keys.rs b/acme_common/src/crypto/openssl_keys.rs index 591fa40..f6aae3f 100644 --- a/acme_common/src/crypto/openssl_keys.rs +++ b/acme_common/src/crypto/openssl_keys.rs @@ -85,6 +85,13 @@ impl KeyPair { pub fn sign(&self, alg: &JwsSignatureAlgorithm, data: &[u8]) -> Result, Error> { let _ = self.key_type.check_alg_compatibility(alg)?; match alg { + JwsSignatureAlgorithm::Hs256 + | JwsSignatureAlgorithm::Hs384 + | JwsSignatureAlgorithm::Hs512 => Err(format!( + "{} key pair cannot be used for the {} signature algorithm", + self.key_type, alg + ) + .into()), JwsSignatureAlgorithm::Rs256 => self.sign_rsa(&MessageDigest::sha256(), data), JwsSignatureAlgorithm::Es256 => self.sign_ecdsa(&HashFunction::Sha256, data), JwsSignatureAlgorithm::Es384 => self.sign_ecdsa(&HashFunction::Sha384, data), diff --git a/acmed/src/jws.rs b/acmed/src/jws.rs index 62d366f..b6bf0b9 100644 --- a/acmed/src/jws.rs +++ b/acmed/src/jws.rs @@ -1,5 +1,5 @@ use acme_common::b64_encode; -use acme_common::crypto::{JwsSignatureAlgorithm, KeyPair}; +use acme_common::crypto::{HashFunction, JwsSignatureAlgorithm, KeyPair}; use acme_common::error::Error; use serde::Serialize; use serde_json::value::Value; @@ -26,6 +26,13 @@ struct JwsProtectedHeaderJwk { url: String, } +#[derive(Serialize)] +struct JwsProtectedHeaderKidNoNonce { + alg: String, + kid: String, + url: String, +} + #[derive(Serialize)] struct JwsProtectedHeaderKid { alg: String, @@ -104,6 +111,41 @@ pub fn encode_kid( get_data(key_pair, sign_alg, &protected, payload) } +pub fn encode_kid_mac( + key: &[u8], + sign_alg: &JwsSignatureAlgorithm, + key_id: &str, + payload: &[u8], + url: &str, +) -> Result { + let protected = JwsProtectedHeaderKidNoNonce { + alg: sign_alg.to_string(), + kid: key_id.to_string(), + url: url.into(), + }; + let protected = serde_json::to_string(&protected)?; + let protected = b64_encode(&protected); + let payload = b64_encode(payload); + let signing_input = format!("{}.{}", protected, payload); + let hash_func = match sign_alg { + JwsSignatureAlgorithm::Hs256 => HashFunction::Sha256, + JwsSignatureAlgorithm::Hs384 => HashFunction::Sha384, + JwsSignatureAlgorithm::Hs512 => HashFunction::Sha512, + _ => { + return Err(format!("{}: not a HMAC-based signature algorithm", sign_alg).into()); + } + }; + let signature = hash_func.hmac(key, signing_input.as_bytes())?; + let signature = b64_encode(&signature); + let data = JwsData { + protected, + payload, + signature, + }; + let str_data = serde_json::to_string(&data)?; + Ok(str_data) +} + #[cfg(test)] mod tests { use super::{encode_jwk, encode_kid};