Skip to content

Commit 8a2bc3b

Browse files
authored
rust: Support client controlled signers (cloudflare#76)
* add `GenerateSignature` trait to enable client controlled signing * bump crates to v0.6.1
1 parent 0b4723f commit 8a2bc3b

5 files changed

Lines changed: 72 additions & 27 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ members = [
77
resolver = "2"
88

99
[workspace.package]
10-
version = "0.6.0"
10+
version = "0.6.1"
1111
authors = [
1212
"Akshat Mahajan <akshat@cloudflare.com>",
1313
"Gauri Baraskar <gbaraskar@cloudflare.com>",
@@ -35,4 +35,4 @@ regex = "1.12.2"
3535
time = { version = "0.3.44" }
3636

3737
# workspace dependencies
38-
web-bot-auth = { version = "0.6.0", path = "./crates/web-bot-auth" }
38+
web-bot-auth = { version = "0.6.1", path = "./crates/web-bot-auth" }

crates/web-bot-auth/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ mod tests {
397397
&mut mytest,
398398
time::Duration::seconds(10),
399399
Algorithm::Ed25519,
400-
&private_key.to_vec(),
400+
&private_key,
401401
)
402402
.unwrap();
403403

crates/web-bot-auth/src/message_signatures.rs

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,60 @@ pub trait UnsignedMessage {
261261
fn register_header_contents(&mut self, signature_input: String, signature_header: String);
262262
}
263263

264+
/// Trait that provides interface to generate signatures given a message and an algorithm.
265+
/// This is implemented for `Vec<u8>` and friends as a batteries-included way to generate
266+
/// signatures from raw key material, but can be implemented for any type of client-controlled
267+
/// signer as well (yubikey, cloud kms, web3 wallet, etc).
268+
pub trait GenerateSignature {
269+
/// Generate signature given the algorithm and the message to sign.
270+
fn generate_signature(
271+
&self,
272+
algorithm: Algorithm,
273+
msg: &[u8],
274+
) -> Result<Vec<u8>, ImplementationError>;
275+
}
276+
277+
impl GenerateSignature for [u8] {
278+
fn generate_signature(
279+
&self,
280+
algorithm: Algorithm,
281+
msg: &[u8],
282+
) -> Result<Vec<u8>, ImplementationError> {
283+
let signature = match algorithm {
284+
Algorithm::Ed25519 => {
285+
use ed25519_dalek::{Signer, SigningKey};
286+
let signing_key_dalek = SigningKey::try_from(self)
287+
.map_err(|_| ImplementationError::InvalidKeyLength)?;
288+
289+
signing_key_dalek.sign(msg).to_vec()
290+
}
291+
other => return Err(ImplementationError::UnsupportedAlgorithm(other)),
292+
};
293+
294+
Ok(signature)
295+
}
296+
}
297+
298+
impl GenerateSignature for Vec<u8> {
299+
fn generate_signature(
300+
&self,
301+
algorithm: Algorithm,
302+
msg: &[u8],
303+
) -> Result<Vec<u8>, ImplementationError> {
304+
self.as_slice().generate_signature(algorithm, msg)
305+
}
306+
}
307+
308+
impl GenerateSignature for [u8; 32] {
309+
fn generate_signature(
310+
&self,
311+
algorithm: Algorithm,
312+
msg: &[u8],
313+
) -> Result<Vec<u8>, ImplementationError> {
314+
self.as_slice().generate_signature(algorithm, msg)
315+
}
316+
}
317+
264318
/// A struct that implements signing. The struct fields here are serialized into the `Signature-Input`
265319
/// header.
266320
pub struct MessageSigner {
@@ -273,7 +327,7 @@ pub struct MessageSigner {
273327
}
274328

275329
impl MessageSigner {
276-
/// Sign the provided method with `signing_key`, setting an expiration value of
330+
/// Sign the provided method with `signer`, setting an expiration value of
277331
/// length `expires` from now (the time of signing).
278332
///
279333
/// # Errors
@@ -285,7 +339,7 @@ impl MessageSigner {
285339
message: &mut impl UnsignedMessage,
286340
expires: Duration,
287341
algorithm: Algorithm,
288-
signing_key: &Vec<u8>,
342+
signer: &(impl GenerateSignature + ?Sized),
289343
) -> Result<(), ImplementationError> {
290344
let components_to_cover = message.fetch_components_to_cover();
291345
let mut sfv_parameters = sfv::Parameters::new();
@@ -361,22 +415,13 @@ impl MessageSigner {
361415
}
362416
.into_ascii()?;
363417

364-
let signature = match algorithm {
365-
Algorithm::Ed25519 => {
366-
use ed25519_dalek::{Signer, SigningKey};
367-
let signing_key_dalek = SigningKey::try_from(signing_key.as_slice())
368-
.map_err(|_| ImplementationError::InvalidKeyLength)?;
369-
370-
sfv::Item {
371-
bare_item: sfv::BareItem::ByteSequence(
372-
signing_key_dalek.sign(signature_base.as_bytes()).to_vec(),
373-
),
374-
params: sfv::Parameters::new(),
375-
}
376-
.serialize_value()
377-
}
378-
other => return Err(ImplementationError::UnsupportedAlgorithm(other)),
379-
};
418+
let signature = sfv::Item {
419+
bare_item: sfv::BareItem::ByteSequence(
420+
signer.generate_signature(algorithm, signature_base.as_bytes())?,
421+
),
422+
params: sfv::Parameters::new(),
423+
}
424+
.serialize_value();
380425

381426
message.register_header_contents(signature_params_content, signature);
382427

@@ -670,7 +715,7 @@ mod tests {
670715
&mut test,
671716
Duration::seconds(10),
672717
Algorithm::Ed25519,
673-
&private_key.to_vec()
718+
&private_key
674719
)
675720
.is_ok()
676721
);

examples/signature-agent-card-and-registry/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ async fn fetch(req: HttpRequest, env: Env, _ctx: Context) -> Result<Response> {
169169
&mut generator,
170170
Duration::seconds(10),
171171
Algorithm::Ed25519,
172-
&(signing_key.as_bytes().to_vec()),
172+
signing_key.as_bytes(),
173173
)
174174
.unwrap();
175175
Ok(response)

0 commit comments

Comments
 (0)