Skip to main content

ssi_cose/
sign1.rs

1use crate::{CborValue, CosePayload, CoseSignatureBytes};
2use coset::{
3    sig_structure_data, CborSerializable, CoseError, CoseSign1, Header, ProtectedHeader,
4    TaggedCborSerializable,
5};
6use serde::{Deserialize, Serialize};
7use std::{borrow::Borrow, ops::Deref};
8
9/// CBOR-encoded `COSE_Sign1` object.
10///
11/// This represents the raw CBOR bytes encoding a [`CoseSign1`] object. The
12/// [`Self::decode`] method can be used to decode into a [`DecodedCoseSign1`]
13/// (similar to `CoseSign1` but with extra information about the payload).
14///
15/// This is the borrowed equivalent of [`CoseSign1BytesBuf`].
16#[derive(Debug, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[repr(transparent)]
18#[serde(transparent)]
19pub struct CoseSign1Bytes([u8]);
20
21impl CoseSign1Bytes {
22    /// Creates a new CBOR-encoded `COSE_Sign1` object from a byte slice.
23    ///
24    /// The bytes are not actually checked. If the bytes are not describing
25    /// a CBOR-encoded `COSE_Sign1` object it will be detected when the
26    /// [`Self::decode`] method is called.
27    pub fn new(bytes: &[u8]) -> &Self {
28        unsafe { std::mem::transmute(bytes) }
29    }
30
31    /// Decodes the CBOR bytes into a [`DecodedCoseSign1`].
32    pub fn decode(&self, tagged: bool) -> Result<DecodedCoseSign1, CoseError> {
33        let cose = if tagged {
34            CoseSign1::from_tagged_slice(&self.0)?
35        } else {
36            CoseSign1::from_slice(&self.0)?
37        };
38
39        Ok(cose.into())
40    }
41
42    /// Returns the raw CBOR bytes.
43    pub fn as_bytes(&self) -> &[u8] {
44        &self.0
45    }
46}
47
48impl AsRef<[u8]> for CoseSign1Bytes {
49    fn as_ref(&self) -> &[u8] {
50        self.as_bytes()
51    }
52}
53
54impl ToOwned for CoseSign1Bytes {
55    type Owned = CoseSign1BytesBuf;
56
57    fn to_owned(&self) -> Self::Owned {
58        CoseSign1BytesBuf(self.0.to_owned())
59    }
60}
61
62/// CBOR-encoded `COSE_Sign1` object buffer.
63///
64/// This represents the raw CBOR bytes encoding a [`CoseSign1`] object. The
65/// [`CoseSign1Bytes::decode`] method can be used to decode into a
66/// [`DecodedCoseSign1`] (similar to `CoseSign1` but with extra information
67/// about the payload).
68///
69/// This is the owned equivalent of [`CoseSign1Bytes`].
70#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
71#[serde(transparent)]
72pub struct CoseSign1BytesBuf(Vec<u8>);
73
74impl CoseSign1BytesBuf {
75    /// Creates a new CBOR-encoded `COSE_Sign1` object from a byte buffer.
76    ///
77    /// The bytes are not actually checked. If the bytes are not describing
78    /// a CBOR-encoded `COSE_Sign1` object it will be detected when the
79    /// [`CoseSign1Bytes::decode`] method is called.
80    pub fn new(bytes: Vec<u8>) -> Self {
81        Self(bytes)
82    }
83
84    /// Creates a new CBOR-encoded `COSE_Sign1` object by encoding the give
85    /// [`CoseSign1`] value.
86    ///
87    /// If `tagged` is set to `true`, the CBOR value will be tagged.
88    pub fn encode(object: impl Into<CoseSign1>, tagged: bool) -> Self {
89        if tagged {
90            Self(TaggedCborSerializable::to_tagged_vec(object.into()).unwrap())
91        } else {
92            Self(CborSerializable::to_vec(object.into()).unwrap())
93        }
94    }
95
96    /// Borrows the value as a [`CoseSign1Bytes`].
97    pub fn as_compact(&self) -> &CoseSign1Bytes {
98        CoseSign1Bytes::new(self.0.as_slice())
99    }
100}
101
102impl Deref for CoseSign1BytesBuf {
103    type Target = CoseSign1Bytes;
104
105    fn deref(&self) -> &Self::Target {
106        self.as_compact()
107    }
108}
109
110impl Borrow<CoseSign1Bytes> for CoseSign1BytesBuf {
111    fn borrow(&self) -> &CoseSign1Bytes {
112        self.as_compact()
113    }
114}
115
116impl From<CborValue> for CoseSign1BytesBuf {
117    fn from(value: CborValue) -> Self {
118        let mut buffer = Vec::new();
119        ciborium::into_writer(&value, &mut buffer).unwrap();
120        Self(buffer)
121    }
122}
123
124impl AsRef<[u8]> for CoseSign1BytesBuf {
125    fn as_ref(&self) -> &[u8] {
126        self.as_bytes()
127    }
128}
129
130impl From<Vec<u8>> for CoseSign1BytesBuf {
131    fn from(value: Vec<u8>) -> Self {
132        Self(value)
133    }
134}
135
136/// Decoded `COSE_Sign1` object.
137pub struct DecodedCoseSign1<T = ()> {
138    /// Signing bytes.
139    pub signing_bytes: UnsignedCoseSign1<T>,
140
141    /// Signature.
142    pub signature: CoseSignatureBytes,
143}
144
145impl<T> DecodedCoseSign1<T> {
146    /// Maps the payload interpretation.
147    ///
148    /// This function can be used to decode the raw payload bytes into a
149    /// proper typed value the application can work with.
150    pub fn map<U>(self, f: impl FnOnce(T, &[u8]) -> U) -> DecodedCoseSign1<U> {
151        DecodedCoseSign1 {
152            signing_bytes: self.signing_bytes.map(f),
153            signature: self.signature,
154        }
155    }
156
157    /// Tries to map the payload interpretation.
158    ///
159    /// This function can be used to decode the raw payload bytes into a
160    /// proper typed value the application can work with.
161    pub fn try_map<U, E>(
162        self,
163        f: impl FnOnce(T, &[u8]) -> Result<U, E>,
164    ) -> Result<DecodedCoseSign1<U>, E> {
165        Ok(DecodedCoseSign1 {
166            signing_bytes: self.signing_bytes.try_map(f)?,
167            signature: self.signature,
168        })
169    }
170}
171
172impl From<CoseSign1> for DecodedCoseSign1 {
173    fn from(value: CoseSign1) -> Self {
174        Self {
175            signing_bytes: UnsignedCoseSign1 {
176                protected: value.protected,
177                unprotected: value.unprotected,
178                payload: PayloadBytes::from_bytes(value.payload.unwrap_or_default()),
179            },
180            signature: CoseSignatureBytes(value.signature),
181        }
182    }
183}
184
185impl<T> From<DecodedCoseSign1<T>> for CoseSign1 {
186    fn from(value: DecodedCoseSign1<T>) -> Self {
187        Self {
188            protected: value.signing_bytes.protected,
189            unprotected: value.signing_bytes.unprotected,
190            payload: Some(value.signing_bytes.payload.into_bytes()),
191            signature: value.signature.into_bytes(),
192        }
193    }
194}
195
196/// Payload and bytes.
197///
198/// Stores the payload value as interpreted by the application (type `T`) and
199/// the original payload bytes.
200///
201/// The original payload bytes are always preserved since they can not always
202/// be deterministically (or cheaply) reconstructed from the typed payload
203/// value.
204#[derive(Clone, PartialEq)]
205pub struct PayloadBytes<T = ()> {
206    /// Original payload bytes.
207    bytes: Vec<u8>,
208
209    /// Interpretation of the payload bytes.
210    value: T,
211}
212
213impl PayloadBytes {
214    /// Creates a new `PayloadBytes` from the bytes.
215    ///
216    /// The interpretation of the bytes will be unit `()`.
217    pub fn from_bytes(bytes: Vec<u8>) -> Self {
218        Self { bytes, value: () }
219    }
220}
221
222impl<T: CosePayload> PayloadBytes<T> {
223    /// Creates a new `PayloadBytes` from the payload, using
224    /// [`CosePayload::payload_bytes`] to reconstruct the payload bytes.
225    pub fn new(value: T) -> Self {
226        Self {
227            bytes: value.payload_bytes().into_owned(),
228            value,
229        }
230    }
231}
232
233impl<T> PayloadBytes<T> {
234    /// Returns the bytes as a slice.
235    pub fn as_bytes(&self) -> &[u8] {
236        &self.bytes
237    }
238
239    /// Maps the payload interpretation.
240    ///
241    /// This function can be used to decode the raw payload bytes into a
242    /// proper typed value the application can work with.
243    pub fn map<U>(self, f: impl FnOnce(T, &[u8]) -> U) -> PayloadBytes<U> {
244        let value = f(self.value, &self.bytes);
245        PayloadBytes {
246            bytes: self.bytes,
247            value,
248        }
249    }
250
251    /// Tries to map the payload interpretation.
252    ///
253    /// This function can be used to decode the raw payload bytes into a
254    /// proper typed value the application can work with.
255    pub fn try_map<U, E>(
256        self,
257        f: impl FnOnce(T, &[u8]) -> Result<U, E>,
258    ) -> Result<PayloadBytes<U>, E> {
259        let value = f(self.value, &self.bytes)?;
260        Ok(PayloadBytes {
261            bytes: self.bytes,
262            value,
263        })
264    }
265
266    /// Forgets about the payload interpretation and returns the raw bytes.
267    pub fn into_bytes(self) -> Vec<u8> {
268        self.bytes
269    }
270}
271
272impl<T> Deref for PayloadBytes<T> {
273    type Target = T;
274
275    fn deref(&self) -> &Self::Target {
276        &self.value
277    }
278}
279
280impl<T> Borrow<T> for PayloadBytes<T> {
281    fn borrow(&self) -> &T {
282        &self.value
283    }
284}
285
286/// `COSE_Sign1` object without the signature.
287#[derive(Clone, PartialEq)]
288pub struct UnsignedCoseSign1<T> {
289    /// Protected header.
290    pub protected: ProtectedHeader,
291
292    /// Unprotected header.
293    pub unprotected: Header,
294
295    /// Payload.
296    pub payload: PayloadBytes<T>,
297}
298
299impl<T> UnsignedCoseSign1<T> {
300    /// Returns the bytes that will be signed.
301    pub fn tbs_data(&self, aad: &[u8]) -> Vec<u8> {
302        sig_structure_data(
303            coset::SignatureContext::CoseSign1,
304            self.protected.clone(),
305            None,
306            aad,
307            self.payload.as_bytes(),
308        )
309    }
310
311    /// Maps the payload interpretation.
312    pub fn map<U>(self, f: impl FnOnce(T, &[u8]) -> U) -> UnsignedCoseSign1<U> {
313        UnsignedCoseSign1 {
314            protected: self.protected,
315            unprotected: self.unprotected,
316            payload: self.payload.map(f),
317        }
318    }
319
320    /// Tries to map the payload interpretation.
321    pub fn try_map<U, E>(
322        self,
323        f: impl FnOnce(T, &[u8]) -> Result<U, E>,
324    ) -> Result<UnsignedCoseSign1<U>, E> {
325        Ok(UnsignedCoseSign1 {
326            protected: self.protected,
327            unprotected: self.unprotected,
328            payload: self.payload.try_map(f)?,
329        })
330    }
331}