Skip to main content

cratestack_cose/envelope/
trait_impl.rs

1//! `CratestackEnvelope` for [`CoseEnvelope`].
2//!
3//! Native only (cratestack#1007): the trait is `Send + Sync` with `Send`
4//! futures, which a `wasm32` signer (`?Send`) cannot be. The typed methods
5//! on `CoseEnvelope` are the whole surface there.
6
7#![cfg(not(target_arch = "wasm32"))]
8
9use std::future::Future;
10
11use bytes::Bytes;
12use cratestack_core::{
13    Binding, BodyShape, CratestackCodec, CratestackContext, CratestackEnvelope, CratestackError,
14    StreamOpener, StreamSealer, VerifiedSigner,
15};
16use serde::Serialize;
17
18use super::{CoseEnvelope, CoseRole};
19use crate::seal::PAYLOAD_CAPACITY_HINT;
20
21impl CratestackEnvelope for CoseEnvelope {
22    /// Unary bodies are `application/cose; cose-type="cose-sign1"` or
23    /// `"cose-mac0"`. Streams are `None`: `chain` mode is P1
24    /// (cratestack#1008), and until then a stream keeps plain cbor-seq, as
25    /// the trait's contract requires when the stream methods return `None`.
26    fn media_type(&self, shape: BodyShape) -> Option<&'static str> {
27        match shape {
28            BodyShape::Unary => Some(self.inner.mode.media_type()),
29            BodyShape::Stream => None,
30        }
31    }
32
33    /// A server seals a response, a client seals a request (see
34    /// [`CoseRole`]). The payload arrives encoded, so it is copied into the
35    /// message once; [`seal_value`](Self::seal_value) avoids that copy.
36    fn seal<'a>(
37        &'a self,
38        payload: Bytes,
39        bind: &'a Binding<'a>,
40    ) -> impl Future<Output = Result<Bytes, CratestackError>> + Send + 'a {
41        let request = self.inner.role == CoseRole::Client;
42        async move {
43            crate::seal::seal(&self.inner, bind, request, payload.len(), |out| {
44                out.extend_from_slice(&payload);
45                Ok(())
46            })
47            .await
48        }
49    }
50
51    /// Overridden to encode in place (ADR 0006 ยง1): `codec.encode_into`
52    /// writes the payload straight into the message buffer, after the room
53    /// reserved for its `bstr` head, and the head is patched in once the
54    /// length is known. The bytes equal `codec.encode` followed by
55    /// [`seal`](Self::seal).
56    fn seal_value<'a, C, T>(
57        &'a self,
58        codec: &'a C,
59        value: &'a T,
60        bind: &'a Binding<'a>,
61    ) -> impl Future<Output = Result<Bytes, CratestackError>> + Send + 'a
62    where
63        C: CratestackCodec,
64        T: Serialize + ?Sized + Sync,
65    {
66        let request = self.inner.role == CoseRole::Client;
67        async move {
68            crate::seal::seal(&self.inner, bind, request, PAYLOAD_CAPACITY_HINT, |out| {
69                codec.encode_into(value, out)
70            })
71            .await
72        }
73    }
74
75    /// A server opens a request (replay checks included), a client opens a
76    /// response. On success the context records a [`VerifiedSigner`] that
77    /// names the key that verified: its thumbprint, its `kid` (the header's,
78    /// which the opener checked is the key's own, copied so the context does
79    /// not keep the body alive) and the algorithm. The payload returned is a
80    /// slice of `body`.
81    fn open<'a>(
82        &'a self,
83        body: Bytes,
84        bind: &'a Binding<'a>,
85        ctx: &'a mut CratestackContext,
86    ) -> impl Future<Output = Result<Bytes, CratestackError>> + Send + 'a {
87        let request = self.inner.role == CoseRole::Server;
88        async move {
89            let opened = crate::open::open(&self.inner, body, bind, request).await?;
90            ctx.record_verified_signer(VerifiedSigner::new(
91                Bytes::copy_from_slice(&opened.kid),
92                opened.thumbprint,
93                opened.alg.id(),
94            ));
95            Ok(opened.payload)
96        }
97    }
98
99    fn stream_sealer(&self, _bind: Binding<'static>) -> Option<Box<dyn StreamSealer>> {
100        None
101    }
102
103    fn stream_opener(&self, _bind: Binding<'static>) -> Option<Box<dyn StreamOpener>> {
104        None
105    }
106}