darkbio_crypto/cose/
types.rs

1// crypto-rs: cryptography primitives and wrappers
2// Copyright 2025 Dark Bio AG. All rights reserved.
3//
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file.
6
7//! COSE structure types with CBOR serialization.
8
9use crate::cbor::Cbor;
10use crate::{xdsa, xhpke};
11
12/// Private COSE header label for Unix timestamp.
13pub const HEADER_TIMESTAMP: i64 = -70002;
14
15/// Protected header for COSE_Sign1.
16#[derive(Debug, Clone, PartialEq, Eq, Cbor)]
17pub struct SigProtectedHeader {
18    /// Algorithm identifier (COSE header label 1)
19    #[cbor(key = 1)]
20    pub algorithm: i64,
21    /// Critical headers that must be understood (COSE header label 2)
22    #[cbor(key = 2)]
23    pub crit: CritHeader,
24    /// Key identifier - signer's fingerprint (COSE header label 4)
25    #[cbor(key = 4)]
26    pub kid: xdsa::Fingerprint,
27    /// Unix timestamp in seconds (private header label)
28    #[cbor(key = -70002)]
29    pub timestamp: i64,
30}
31
32/// Critical headers list per RFC 9052.
33/// Implementations must reject messages with unknown crit labels.
34#[derive(Debug, Clone, PartialEq, Eq, Cbor)]
35#[cbor(array)]
36pub struct CritHeader {
37    /// HeaderTimestamp label - must be understood
38    pub timestamp: i64,
39}
40
41/// Protected header for COSE_Encrypt0.
42#[derive(Debug, Clone, PartialEq, Eq, Cbor)]
43pub struct EncProtectedHeader {
44    /// Algorithm identifier (COSE header label 1)
45    #[cbor(key = 1)]
46    pub algorithm: i64,
47    /// Key identifier - recipient's fingerprint (COSE header label 4)
48    #[cbor(key = 4)]
49    pub kid: xhpke::Fingerprint,
50}
51
52/// Empty unprotected header map (for COSE_Sign1).
53#[derive(Debug, Clone, PartialEq, Eq, Cbor)]
54pub struct EmptyHeader {}
55
56/// Unprotected header containing the encapsulated key (for COSE_Encrypt0).
57#[derive(Debug, Clone, PartialEq, Eq, Cbor)]
58pub struct EncapKeyHeader {
59    /// Encapsulated key (COSE header label -4)
60    #[cbor(key = -4)]
61    pub encap_key: Vec<u8>,
62}
63
64/// COSE_Sign1 structure per RFC 9052 Section 4.2.
65///
66/// ```text
67/// COSE_Sign1 = [
68///     protected:   bstr,
69///     unprotected: header_map,
70///     payload:     bstr / null,
71///     signature:   bstr
72/// ]
73/// ```
74#[derive(Debug, Clone, PartialEq, Eq, Cbor)]
75#[cbor(array)]
76pub struct CoseSign1 {
77    /// Protected header (CBOR-encoded map, wrapped as bstr)
78    pub protected: Vec<u8>,
79    /// Unprotected header map (empty for signatures)
80    pub unprotected: EmptyHeader,
81    /// Payload bytes (null for detached payload)
82    pub payload: Option<Vec<u8>>,
83    /// Signature (fixed size for xDSA)
84    pub signature: xdsa::Signature,
85}
86
87/// COSE_Encrypt0 structure per RFC 9052 Section 5.2.
88///
89/// ```text
90/// COSE_Encrypt0 = [
91///     protected:   bstr,
92///     unprotected: header_map,
93///     ciphertext:  bstr
94/// ]
95/// ```
96#[derive(Debug, Clone, PartialEq, Eq, Cbor)]
97#[cbor(array)]
98pub struct CoseEncrypt0 {
99    /// Protected header (CBOR-encoded map, wrapped as bstr)
100    pub protected: Vec<u8>,
101    /// Unprotected header map (contains encapsulated key)
102    pub unprotected: EncapKeyHeader,
103    /// Ciphertext bytes
104    pub ciphertext: Vec<u8>,
105}
106
107/// Sig_structure for computing signatures per RFC 9052 Section 4.4.
108///
109/// ```text
110/// Sig_structure = [
111///     context:        "Signature1",
112///     body_protected: bstr,
113///     external_aad:   bstr,
114///     payload:        bstr
115/// ]
116/// ```
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub struct SigStructure<'a> {
119    pub context: &'static str,
120    pub protected: &'a [u8],
121    pub external_aad: &'a [u8],
122    pub payload: &'a [u8],
123}
124
125impl crate::cbor::Encode for SigStructure<'_> {
126    fn encode_cbor(&self) -> Vec<u8> {
127        let mut encoder = crate::cbor::Encoder::new();
128        encoder.encode_array_header(4);
129        encoder.encode_text(self.context);
130        encoder.encode_bytes(self.protected);
131        encoder.encode_bytes(self.external_aad);
132        encoder.encode_bytes(self.payload);
133        encoder.finish()
134    }
135}
136
137/// Enc_structure for computing AAD per RFC 9052 Section 5.3.
138///
139/// ```text
140/// Enc_structure = [
141///     context:      "Encrypt0",
142///     protected:    bstr,
143///     external_aad: bstr
144/// ]
145/// ```
146#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147pub struct EncStructure<'a> {
148    pub context: &'static str,
149    pub protected: &'a [u8],
150    pub external_aad: &'a [u8],
151}
152
153impl crate::cbor::Encode for EncStructure<'_> {
154    fn encode_cbor(&self) -> Vec<u8> {
155        let mut encoder = crate::cbor::Encoder::new();
156        encoder.encode_array_header(3);
157        encoder.encode_text(self.context);
158        encoder.encode_bytes(self.protected);
159        encoder.encode_bytes(self.external_aad);
160        encoder.finish()
161    }
162}