identity_jose/jws/encoding/
encoder.rs1use self::encoder_state::ReadyState;
5use self::encoder_state::RecipientProcessingState;
6
7use crate::error::Error;
8use crate::error::Result;
9use crate::jws::encoding::utils::JwsSignature;
10use crate::jws::CharSet;
11use crate::jws::JwsHeader;
12use crate::jws::Recipient;
13use std::borrow::Cow;
14
15use crate::jwu;
16
17use super::utils;
18use super::utils::Flatten;
19use super::utils::General;
20use super::utils::MaybeEncodedPayload;
21use super::utils::SigningData;
22
23pub struct CompactJwsEncoder<'a> {
27 protected_header: String,
28 processed_payload: Option<Cow<'a, str>>,
29 signing_input: Box<[u8]>,
30}
31
32#[derive(Debug, Copy, Clone)]
33pub enum CompactJwsEncodingOptions {
36 NonDetached {
38 charset_requirements: CharSet,
40 },
41 Detached,
43}
44
45impl<'payload> CompactJwsEncoder<'payload> {
46 pub fn new(payload: &'payload [u8], protected_header: &JwsHeader) -> Result<Self> {
53 Self::new_with_options(
54 payload,
55 protected_header,
56 CompactJwsEncodingOptions::NonDetached {
57 charset_requirements: CharSet::Default,
58 },
59 )
60 }
61
62 pub fn new_with_options(
65 payload: &'payload [u8],
66 protected_header: &JwsHeader,
67 options: CompactJwsEncodingOptions,
68 ) -> Result<Self> {
69 Self::validate_header(protected_header)?;
70 let encoded_protected_header: String = jwu::encode_b64_json(protected_header)?;
71 let maybe_encoded: MaybeEncodedPayload<'_> = MaybeEncodedPayload::encode_if_b64(payload, Some(protected_header));
72 let signing_input: Box<[u8]> =
73 jwu::create_message(encoded_protected_header.as_bytes(), maybe_encoded.as_bytes()).into();
74
75 let processed_payload: Option<Cow<'payload, str>> = {
76 if let CompactJwsEncodingOptions::NonDetached { charset_requirements } = options {
77 Some(maybe_encoded.into_non_detached(|input| charset_requirements.validate(input))?)
78 } else {
79 None
80 }
81 };
82
83 Ok(Self {
84 protected_header: encoded_protected_header,
85 processed_payload,
86 signing_input,
87 })
88 }
89
90 pub fn signing_input(&self) -> &[u8] {
94 &self.signing_input
95 }
96
97 fn validate_header(protected_header: &JwsHeader) -> Result<()> {
98 jwu::validate_jws_headers(Some(protected_header), None)
99 }
100
101 pub fn into_jws(self, signature: &[u8]) -> String {
105 let signature = jwu::encode_b64(signature);
106 if let Some(payload) = self.processed_payload {
107 format!("{}.{}.{}", self.protected_header, payload, &signature)
108 } else {
109 format!("{}..{}", self.protected_header, &signature)
110 }
111 }
112}
113
114pub struct FlattenedJwsEncoder<'payload, 'unprotected> {
126 processed_payload: Option<Cow<'payload, str>>,
127 signing_data: SigningData,
128 unprotected_header: Option<&'unprotected JwsHeader>,
129}
130
131impl<'payload, 'unprotected> FlattenedJwsEncoder<'payload, 'unprotected> {
132 pub fn new(payload: &'payload [u8], recipient: Recipient<'unprotected>, detached: bool) -> Result<Self> {
135 utils::validate_headers_json_serialization(recipient)?;
136 let maybe_encoded: MaybeEncodedPayload<'_> = MaybeEncodedPayload::encode_if_b64(payload, recipient.protected);
137 let signing_data: SigningData = SigningData::new(maybe_encoded.as_bytes(), recipient.protected)?;
138 let processed_payload: Option<Cow<'payload, str>> = if !detached {
139 Some(maybe_encoded.into_non_detached(|bytes| std::str::from_utf8(bytes).map_err(Error::InvalidUtf8))?)
142 } else {
143 None
144 };
145
146 Ok(Self {
147 processed_payload,
148 signing_data,
149 unprotected_header: recipient.unprotected,
150 })
151 }
152
153 pub fn signing_input(&self) -> &[u8] {
157 &self.signing_data.signing_input
158 }
159
160 pub fn into_jws(self, signature: &[u8]) -> Result<String> {
164 let FlattenedJwsEncoder {
165 processed_payload,
166 signing_data,
167 unprotected_header,
168 } = self;
169
170 Flatten {
171 payload: processed_payload.as_deref(),
172 signature: signing_data.into_signature(signature, unprotected_header),
173 }
174 .to_json()
175 }
176}
177
178mod encoder_state {
183 use super::utils::SigningData;
184 use crate::jws::JwsHeader;
185 pub struct ReadyState;
186 pub struct RecipientProcessingState<'unprotected> {
187 pub(super) signing_data: SigningData,
188 pub(super) unprotected_header: Option<&'unprotected JwsHeader>,
189 }
190}
191
192pub type RecipientProcessingEncoder<'payload, 'unprotected> =
194 GeneralJwsEncoder<'payload, 'unprotected, RecipientProcessingState<'unprotected>>;
195
196pub struct GeneralJwsEncoder<'payload, 'unprotected, STATE = ReadyState> {
198 partially_processed_payload: Cow<'payload, [u8]>,
199 signatures: Vec<JwsSignature<'unprotected>>,
200 detached: bool,
201 b64: bool,
202 state: STATE,
203}
204
205impl<'payload, 'unprotected> GeneralJwsEncoder<'payload, 'unprotected> {
206 pub fn new(
210 payload: &'payload [u8],
211 first_recipient: Recipient<'unprotected>,
212 detached: bool,
213 ) -> Result<RecipientProcessingEncoder<'payload, 'unprotected>> {
214 utils::validate_headers_json_serialization(first_recipient)?;
215 let partially_processed_payload: Cow<'payload, [u8]> =
216 MaybeEncodedPayload::encode_if_b64(payload, first_recipient.protected).into();
217 let signing_data = SigningData::new(&partially_processed_payload, first_recipient.protected)?;
218 Ok(RecipientProcessingEncoder {
219 partially_processed_payload,
220 signatures: Vec::new(),
221 detached,
222 b64: jwu::extract_b64(first_recipient.protected),
223 state: RecipientProcessingState {
224 signing_data,
225 unprotected_header: first_recipient.unprotected,
226 },
227 })
228 }
229
230 pub fn add_recipient(
233 self,
234 recipient: Recipient<'unprotected>,
235 ) -> Result<RecipientProcessingEncoder<'payload, 'unprotected>> {
236 let new_b64 = jwu::extract_b64(recipient.protected);
238 if new_b64 != self.b64 {
239 return Err(Error::InvalidParam("b64"));
240 };
241 utils::validate_headers_json_serialization(recipient)?;
243
244 let signing_data = SigningData::new(&self.partially_processed_payload, recipient.protected)?;
245 let state = RecipientProcessingState {
246 signing_data,
247 unprotected_header: recipient.unprotected,
248 };
249 let Self {
250 partially_processed_payload,
251 signatures,
252 detached,
253 b64,
254 ..
255 } = self;
256 Ok(RecipientProcessingEncoder {
257 partially_processed_payload,
258 signatures,
259 detached,
260 b64,
261 state,
262 })
263 }
264
265 pub fn into_jws(self) -> Result<String> {
268 let GeneralJwsEncoder {
269 partially_processed_payload,
270 signatures,
271 detached,
272 ..
273 } = self;
274 let general: General<'_, '_> = {
275 if detached {
276 General {
277 payload: None,
278 signatures,
279 }
280 } else {
281 General {
282 payload: Some(
283 std::str::from_utf8(&partially_processed_payload).map_err(|_| Error::InvalidContent("invalid utf8"))?,
284 ),
285 signatures,
286 }
287 }
288 };
289 general.to_json()
290 }
291}
292
293impl<'payload, 'unprotected> RecipientProcessingEncoder<'payload, 'unprotected> {
294 pub fn signing_input(&self) -> &[u8] {
300 &self.state.signing_data.signing_input
301 }
302
303 pub fn set_signature(self, signature: &[u8]) -> GeneralJwsEncoder<'payload, 'unprotected> {
306 let Self {
307 partially_processed_payload,
308 mut signatures,
309 b64,
310 detached,
311 state: RecipientProcessingState {
312 unprotected_header,
313 signing_data,
314 },
315 } = self;
316 let new_signature = signing_data.into_signature(signature, unprotected_header);
317 signatures.push(new_signature);
318 GeneralJwsEncoder {
319 partially_processed_payload,
320 signatures,
321 b64,
322 detached,
323 state: ReadyState {},
324 }
325 }
326}