pub trait CoseSigner: MaybeSendSync + 'static {
// Required methods
fn alg(&self) -> CoseAlg;
fn kid(&self) -> &[u8] ⓘ;
fn sign<'life0, 'life1, 'async_trait>(
&'life0 self,
to_be_signed: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, CratestackError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait;
// Provided method
fn sign_chunks(
&self,
to_be_signed: &[&[u8]],
) -> Option<Result<Vec<u8>, CratestackError>> { ... }
}Expand description
Produces signatures (or MAC tags) without handing out the key.
Target-split (cratestack#1007, the same split as the client’s
RequestAuthorizer): natively the trait is Send + Sync and its futures
are Send, exactly as before. On wasm32 it is neither, because a
signer there is a WebCrypto call or a JS callback, and a JsFuture is
never Send. An implementation that builds for both targets writes
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] and
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))].
KeyProvider::resolve_signing_key returns raw key bytes, which fits
HMAC but not an asymmetric key that lives in an HSM or a KMS (§1: “keys
are signed with, not exported”). A signer is asked to sign the complete
Sig_structure / MAC_structure and nothing else.
#[async_trait] rather than impl Future: signers are held as
Arc<dyn CoseSigner> so one envelope type serves every key backend, and
a remote signer is a network call anyway, next to which the boxed
future is noise.
The envelope checks what a signer returns: a kid that is not 8 bytes,
an algorithm that does not fit the envelope’s mode, or a signature whose
length is not CoseAlg::signature_len is refused with a 500 before
anything reaches the wire. An ESP256 signature is normalised to low-s.
Required Methods§
Sourcefn sign<'life0, 'life1, 'async_trait>(
&'life0 self,
to_be_signed: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, CratestackError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn sign<'life0, 'life1, 'async_trait>(
&'life0 self,
to_be_signed: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, CratestackError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
Sign (or MAC) to_be_signed. For HMAC 256/64 this returns the
truncated 8-byte tag. An error is a backend failure (a 500).
Provided Methods§
Sourcefn sign_chunks(
&self,
to_be_signed: &[&[u8]],
) -> Option<Result<Vec<u8>, CratestackError>>
fn sign_chunks( &self, to_be_signed: &[&[u8]], ) -> Option<Result<Vec<u8>, CratestackError>>
Sign the concatenation of to_be_signed without it ever being
built in one buffer, or None to have the envelope build it and
call sign instead (the default).
This is how the in-process signers (HMAC, ESP256 and Ed25519) keep
the payload in the one place seal_value encoded it (maintainer
decisions on cratestack#1005): each hashes its input (Ed25519 twice),
so they can be fed the structure’s pieces, which lie in the output
buffer, on the stack and in the header. The design has to stay
dyn-compatible (signers are Arc<dyn CoseSigner>), which rules out
a generic “feed this hasher” method; a slice of slices is the
dyn-compatible form of a stream of bytes. It is a provided method,
so adding it broke no signer. It is synchronous because it exists
for in-process signers only: a remote signer needs the bytes (or a
digest) on the wire and keeps None, so a KMS or HSM signer is
handed the contiguous structure.
An override must return exactly what sign returns for the
concatenated bytes; the shared vectors check that for the shipped
signers.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".