diff --git a/Cargo.lock b/Cargo.lock index 3571f62..63cf43e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -595,7 +595,7 @@ dependencies = [ [[package]] name = "http-signature-directory" -version = "0.6.0" +version = "0.6.1" dependencies = [ "clap", "env_logger", @@ -1246,7 +1246,7 @@ dependencies = [ [[package]] name = "rust-examples" -version = "0.6.0" +version = "0.6.1" dependencies = [ "indexmap", "time", @@ -1463,7 +1463,7 @@ dependencies = [ [[package]] name = "signature-agent-card-and-registry" -version = "0.6.0" +version = "0.6.1" dependencies = [ "base64", "ed25519-dalek", @@ -1892,7 +1892,7 @@ dependencies = [ [[package]] name = "web-bot-auth" -version = "0.6.0" +version = "0.6.1" dependencies = [ "base64", "data-url", diff --git a/Cargo.toml b/Cargo.toml index ded13a4..d176599 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.6.0" +version = "0.6.1" authors = [ "Akshat Mahajan ", "Gauri Baraskar ", @@ -35,4 +35,4 @@ regex = "1.12.2" time = { version = "0.3.44" } # workspace dependencies -web-bot-auth = { version = "0.6.0", path = "./crates/web-bot-auth" } +web-bot-auth = { version = "0.6.1", path = "./crates/web-bot-auth" } diff --git a/crates/web-bot-auth/src/lib.rs b/crates/web-bot-auth/src/lib.rs index 735ea17..243ecc2 100644 --- a/crates/web-bot-auth/src/lib.rs +++ b/crates/web-bot-auth/src/lib.rs @@ -397,7 +397,7 @@ mod tests { &mut mytest, time::Duration::seconds(10), Algorithm::Ed25519, - &private_key.to_vec(), + &private_key, ) .unwrap(); diff --git a/crates/web-bot-auth/src/message_signatures.rs b/crates/web-bot-auth/src/message_signatures.rs index c4c7197..fb3437b 100644 --- a/crates/web-bot-auth/src/message_signatures.rs +++ b/crates/web-bot-auth/src/message_signatures.rs @@ -261,6 +261,60 @@ pub trait UnsignedMessage { fn register_header_contents(&mut self, signature_input: String, signature_header: String); } +/// Trait that provides interface to generate signatures given a message and an algorithm. +/// This is implemented for `Vec` and friends as a batteries-included way to generate +/// signatures from raw key material, but can be implemented for any type of client-controlled +/// signer as well (yubikey, cloud kms, web3 wallet, etc). +pub trait GenerateSignature { + /// Generate signature given the algorithm and the message to sign. + fn generate_signature( + &self, + algorithm: Algorithm, + msg: &[u8], + ) -> Result, ImplementationError>; +} + +impl GenerateSignature for [u8] { + fn generate_signature( + &self, + algorithm: Algorithm, + msg: &[u8], + ) -> Result, ImplementationError> { + let signature = match algorithm { + Algorithm::Ed25519 => { + use ed25519_dalek::{Signer, SigningKey}; + let signing_key_dalek = SigningKey::try_from(self) + .map_err(|_| ImplementationError::InvalidKeyLength)?; + + signing_key_dalek.sign(msg).to_vec() + } + other => return Err(ImplementationError::UnsupportedAlgorithm(other)), + }; + + Ok(signature) + } +} + +impl GenerateSignature for Vec { + fn generate_signature( + &self, + algorithm: Algorithm, + msg: &[u8], + ) -> Result, ImplementationError> { + self.as_slice().generate_signature(algorithm, msg) + } +} + +impl GenerateSignature for [u8; 32] { + fn generate_signature( + &self, + algorithm: Algorithm, + msg: &[u8], + ) -> Result, ImplementationError> { + self.as_slice().generate_signature(algorithm, msg) + } +} + /// A struct that implements signing. The struct fields here are serialized into the `Signature-Input` /// header. pub struct MessageSigner { @@ -273,7 +327,7 @@ pub struct MessageSigner { } impl MessageSigner { - /// Sign the provided method with `signing_key`, setting an expiration value of + /// Sign the provided method with `signer`, setting an expiration value of /// length `expires` from now (the time of signing). /// /// # Errors @@ -285,7 +339,7 @@ impl MessageSigner { message: &mut impl UnsignedMessage, expires: Duration, algorithm: Algorithm, - signing_key: &Vec, + signer: &(impl GenerateSignature + ?Sized), ) -> Result<(), ImplementationError> { let components_to_cover = message.fetch_components_to_cover(); let mut sfv_parameters = sfv::Parameters::new(); @@ -361,22 +415,13 @@ impl MessageSigner { } .into_ascii()?; - let signature = match algorithm { - Algorithm::Ed25519 => { - use ed25519_dalek::{Signer, SigningKey}; - let signing_key_dalek = SigningKey::try_from(signing_key.as_slice()) - .map_err(|_| ImplementationError::InvalidKeyLength)?; - - sfv::Item { - bare_item: sfv::BareItem::ByteSequence( - signing_key_dalek.sign(signature_base.as_bytes()).to_vec(), - ), - params: sfv::Parameters::new(), - } - .serialize_value() - } - other => return Err(ImplementationError::UnsupportedAlgorithm(other)), - }; + let signature = sfv::Item { + bare_item: sfv::BareItem::ByteSequence( + signer.generate_signature(algorithm, signature_base.as_bytes())?, + ), + params: sfv::Parameters::new(), + } + .serialize_value(); message.register_header_contents(signature_params_content, signature); @@ -670,7 +715,7 @@ mod tests { &mut test, Duration::seconds(10), Algorithm::Ed25519, - &private_key.to_vec() + &private_key ) .is_ok() ); diff --git a/examples/signature-agent-card-and-registry/src/lib.rs b/examples/signature-agent-card-and-registry/src/lib.rs index c998270..38050ae 100644 --- a/examples/signature-agent-card-and-registry/src/lib.rs +++ b/examples/signature-agent-card-and-registry/src/lib.rs @@ -169,7 +169,7 @@ async fn fetch(req: HttpRequest, env: Env, _ctx: Context) -> Result { &mut generator, Duration::seconds(10), Algorithm::Ed25519, - &(signing_key.as_bytes().to_vec()), + signing_key.as_bytes(), ) .unwrap(); Ok(response)