Skip to main content

co_didcomm/messages/
jwe.rs

1use base64_url::{decode, encode};
2use rand::{prelude::SliceRandom, Rng};
3
4use crate::{
5    messages::helpers::{create_fallback_getter, serialization_base64_jwm_header},
6    Jwk, JwmHeader,
7};
8
9/// This struct presents single recipient of JWE `recipients` collection.
10/// Each recipient should have same body cypher key encrypted with shared secret.
11/// [Spec](https://tools.ietf.org/html/rfc7516#section-7.2.1)
12///
13#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
14pub struct Recipient {
15    pub header: Jwk,
16
17    pub encrypted_key: String,
18}
19
20impl Recipient {
21    pub fn new(header: Jwk, encrypted_key: String) -> Self {
22        Recipient {
23            header,
24            encrypted_key,
25        }
26    }
27}
28
29/// JWE representation of `Message` with public header.
30/// Can be serialized to JSON or Compact representations and from same.
31#[derive(Serialize, Deserialize, Clone, Default)]
32pub struct Jwe {
33    /// integrity protected header elements
34    #[serde(default)]
35    #[serde(skip_serializing_if = "Option::is_none")]
36    #[serde(with = "serialization_base64_jwm_header")]
37    pub protected: Option<JwmHeader>,
38
39    /// header elements that are not integrity protected
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub unprotected: Option<JwmHeader>,
42
43    /// Top-level recipient data for flat JWE JSON messages.
44    /// Will be ignored if `recipients` is not `None`
45    #[serde(flatten)]
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub recipient: Option<Recipient>,
48
49    /// Pre-recipient data for flat JWE JSON messages.
50    /// If not `None`, will be preferred over `recipient`.
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub recipients: Option<Vec<Recipient>>,
53
54    /// Encrypted data of JWE as base64 encoded String
55    ciphertext: String,
56
57    /// Initial vector for encryption as base64 encoded String
58    iv: String,
59
60    /// base64 encoded JWE authentication tag
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub tag: Option<String>,
63}
64
65impl Jwe {
66    /// Constructor, which should be used after message is encrypted.
67    pub fn new(
68        unprotected: Option<JwmHeader>,
69        recipients: Option<Vec<Recipient>>,
70        ciphertext: impl AsRef<[u8]>,
71        protected: Option<JwmHeader>,
72        tag: Option<impl AsRef<[u8]>>,
73        iv_input: Option<String>,
74    ) -> Self {
75        Jwe {
76            unprotected,
77            recipients,
78            ciphertext: encode(ciphertext.as_ref()),
79            protected,
80            iv: Self::ensure_iv(iv_input),
81            tag: tag.map(|tag_unencoded| encode(tag_unencoded.as_ref())),
82            recipient: None,
83        }
84    }
85
86    /// Constructor for creating a flat JWE JSON
87    pub fn new_flat(
88        unprotected: Option<JwmHeader>,
89        recipient: Recipient,
90        ciphertext: impl AsRef<[u8]>,
91        protected: Option<JwmHeader>,
92        tag: Option<impl AsRef<[u8]>>,
93        iv_input: Option<String>,
94    ) -> Self {
95        Jwe {
96            unprotected,
97            recipients: None,
98            ciphertext: encode(ciphertext.as_ref()),
99            protected,
100            iv: Self::ensure_iv(iv_input),
101            tag: tag.map(|tag_unencoded| encode(tag_unencoded.as_ref())),
102            recipient: Some(recipient),
103        }
104    }
105
106    /// Generate new random IV as String
107    pub fn generate_iv() -> String {
108        let mut rng = rand::thread_rng();
109        let mut a = rng.gen::<[u8; 24]>().to_vec();
110        a.shuffle(&mut rng);
111        encode(&a)
112    }
113
114    /// Gets `iv` as byte array.
115    pub fn get_iv(&self) -> impl AsRef<[u8]> {
116        decode(&self.iv).unwrap()
117    }
118
119    /// Getter for ciphered payload of JWE.
120    pub fn get_payload(&self) -> Vec<u8> {
121        decode(&self.ciphertext).unwrap()
122    }
123
124    create_fallback_getter!(protected, unprotected, alg, String);
125
126    create_fallback_getter!(protected, unprotected, cty, String);
127
128    create_fallback_getter!(protected, unprotected, enc, String);
129
130    create_fallback_getter!(protected, unprotected, epk, Jwk);
131
132    create_fallback_getter!(protected, unprotected, jku, String);
133
134    create_fallback_getter!(protected, unprotected, jwk, Jwk);
135
136    create_fallback_getter!(protected, unprotected, kid, String);
137
138    create_fallback_getter!(protected, unprotected, skid, String);
139
140    /// Gets initial vector from option or creates a new one.
141    ///
142    /// # Arguments
143    ///
144    /// * `iv_input` - an option that may contain an initial vector
145    fn ensure_iv(iv_input: Option<String>) -> String {
146        iv_input.unwrap_or_else(|| {
147            let mut rng = rand::thread_rng();
148            let mut a = rng.gen::<[u8; 24]>().to_vec();
149            a.shuffle(&mut rng);
150            encode(&a)
151        })
152    }
153}
154
155#[test]
156fn default_jwe_with_random_iv() {
157    // Arrange
158    let not_expected: Vec<u8> = vec![0; 24];
159    // Act
160    let jwe = Jwe::new(None, None, vec![], None, Some(vec![]), None);
161    // Assert
162    assert_ne!(not_expected, decode(&jwe.iv).unwrap());
163}