1use base64::Engine;
2use core::fmt;
3use std::{ops::Deref, str::FromStr};
4
5use crate::{
6 utils::is_url_safe_base64_char, DecodeError, DecodedJws, Header, InvalidJws, JwsSlice,
7};
8
9#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
20#[repr(transparent)]
21pub struct JwsStr(JwsSlice);
22
23impl JwsStr {
24 pub fn new<T: ?Sized + AsRef<[u8]>>(data: &T) -> Result<&Self, InvalidJws<&T>> {
25 let bytes = data.as_ref();
26 match std::str::from_utf8(bytes) {
27 Ok(_) => {
28 let _ = JwsSlice::new(bytes).map_err(|_| InvalidJws(data))?;
29 Ok(unsafe { Self::new_unchecked(bytes) })
30 }
31 Err(_) => Err(InvalidJws(data)),
32 }
33 }
34
35 pub const fn validate(bytes: &[u8]) -> bool {
36 Self::validate_range(bytes, 0, bytes.len())
37 }
38
39 pub const fn validate_range(bytes: &[u8], mut i: usize, end: usize) -> bool {
40 let mut j = if end > bytes.len() { bytes.len() } else { end };
41
42 loop {
44 if i >= j {
45 return false;
47 }
48
49 if bytes[i] == b'.' {
50 break;
51 }
52
53 if !is_url_safe_base64_char(bytes[i]) {
54 return false;
55 }
56
57 i += 1
58 }
59
60 if i >= j {
62 return false;
63 }
64 j -= 1;
65 loop {
66 if i >= j {
67 return false;
69 }
70
71 if bytes[j] == b'.' {
72 break;
73 }
74
75 if !is_url_safe_base64_char(bytes[j]) {
76 return false;
77 }
78
79 j -= 1
80 }
81
82 i += 1;
84 let payload_bytes = unsafe { std::slice::from_raw_parts(bytes.as_ptr().add(i), j - i) };
85
86 std::str::from_utf8(payload_bytes).is_ok()
87 }
88
89 pub const unsafe fn new_unchecked(data: &[u8]) -> &Self {
96 std::mem::transmute(data)
97 }
98
99 pub fn as_str(&self) -> &str {
100 unsafe {
101 std::str::from_utf8_unchecked(self.0.as_bytes())
104 }
105 }
106}
107
108impl Deref for JwsStr {
109 type Target = JwsSlice;
110
111 fn deref(&self) -> &Self::Target {
112 &self.0
113 }
114}
115
116impl fmt::Display for JwsStr {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 self.as_str().fmt(f)
119 }
120}
121
122impl fmt::Debug for JwsStr {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 self.as_str().fmt(f)
125 }
126}
127
128impl serde::Serialize for JwsStr {
129 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
130 where
131 S: serde::Serializer,
132 {
133 self.as_str().serialize(serializer)
134 }
135}
136
137impl PartialEq<str> for JwsStr {
138 fn eq(&self, other: &str) -> bool {
139 self.as_str() == other
140 }
141}
142
143impl PartialEq<String> for JwsStr {
144 fn eq(&self, other: &String) -> bool {
145 self.as_str() == other
146 }
147}
148
149impl<'a> PartialEq<String> for &'a JwsStr {
150 fn eq(&self, other: &String) -> bool {
151 self.as_str() == other
152 }
153}
154
155impl PartialEq<JwsStr> for str {
156 fn eq(&self, other: &JwsStr) -> bool {
157 self == other.as_str()
158 }
159}
160
161impl PartialEq<JwsStr> for String {
162 fn eq(&self, other: &JwsStr) -> bool {
163 self == other.as_str()
164 }
165}
166
167impl<'a> PartialEq<&'a JwsStr> for String {
168 fn eq(&self, other: &&'a JwsStr) -> bool {
169 self == other.as_str()
170 }
171}
172
173#[derive(Clone, serde::Serialize)]
181#[serde(transparent)]
182pub struct JwsString(String);
183
184impl JwsString {
185 pub fn new(bytes: Vec<u8>) -> Result<Self, InvalidJws<Vec<u8>>> {
186 match String::from_utf8(bytes) {
187 Ok(string) => {
188 if JwsSlice::validate(string.as_bytes()) {
189 Ok(Self(string))
190 } else {
191 Err(InvalidJws(string.into_bytes()))
192 }
193 }
194 Err(e) => Err(InvalidJws(e.into_bytes())),
195 }
196 }
197
198 pub fn from_string(string: String) -> Result<Self, InvalidJws<String>> {
199 if JwsSlice::validate(string.as_bytes()) {
200 Ok(Self(string))
201 } else {
202 Err(InvalidJws(string))
203 }
204 }
205
206 pub unsafe fn new_unchecked(bytes: Vec<u8>) -> Self {
211 Self(String::from_utf8_unchecked(bytes))
212 }
213
214 pub fn new_detached(header: Header, b64_signature: &[u8]) -> Result<Self, InvalidJws<Vec<u8>>> {
218 let mut bytes = header.encode().into_bytes();
219 bytes.extend(b"..");
220 bytes.extend(b64_signature.iter().copied());
221 Self::new(bytes)
222 }
223
224 pub fn encode_detached(header: Header, signature: &[u8]) -> Self {
228 let b64_signature = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(signature);
229 Self::new_detached(header, b64_signature.as_bytes()).unwrap()
230 }
231
232 pub fn encode_from_signing_bytes_and_signature(
234 signing_bytes: Vec<u8>,
235 signature: &[u8],
236 ) -> Result<Self, InvalidJws<Vec<u8>>> {
237 let b64_signature = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(signature);
238 let mut bytes = signing_bytes;
239 bytes.push(b'.');
240 bytes.extend_from_slice(b64_signature.as_bytes());
241 Self::new(bytes)
242 }
243
244 pub fn from_signing_bytes_and_signature(
245 signing_bytes: Vec<u8>,
246 signature: impl IntoIterator<Item = u8>,
247 ) -> Result<Self, InvalidJws<Vec<u8>>> {
248 let mut bytes = signing_bytes;
249 bytes.push(b'.');
250 bytes.extend(signature);
251 Self::new(bytes)
252 }
253
254 pub unsafe fn from_signing_bytes_and_signature_unchecked(
259 signing_bytes: Vec<u8>,
260 signature: Vec<u8>,
261 ) -> Self {
262 let mut bytes = signing_bytes;
263 bytes.push(b'.');
264 bytes.extend(signature);
265 Self::new_unchecked(bytes)
266 }
267
268 pub fn as_compact_jws_str(&self) -> &JwsStr {
269 unsafe { JwsStr::new_unchecked(self.0.as_bytes()) }
270 }
271
272 pub fn into_signing_bytes(mut self) -> String {
273 self.0.truncate(self.payload_end()); self.0
275 }
276
277 pub fn into_string(self) -> String {
278 self.0
279 }
280
281 pub fn into_decoded(self) -> Result<DecodedJws<'static>, DecodeError> {
284 Ok(self.decode()?.into_owned())
285 }
286}
287
288impl Deref for JwsString {
289 type Target = JwsStr;
290
291 fn deref(&self) -> &Self::Target {
292 self.as_compact_jws_str()
293 }
294}
295
296impl fmt::Display for JwsString {
297 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 self.as_str().fmt(f)
299 }
300}
301
302impl fmt::Debug for JwsString {
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 self.as_str().fmt(f)
305 }
306}
307
308impl FromStr for JwsString {
309 type Err = InvalidJws;
310
311 fn from_str(s: &str) -> Result<Self, Self::Err> {
312 Self::from_string(s.to_owned())
313 }
314}
315
316impl TryFrom<String> for JwsString {
317 type Error = InvalidJws<String>;
318
319 fn try_from(value: String) -> Result<Self, Self::Error> {
320 Self::from_string(value)
321 }
322}
323
324impl<'de> serde::Deserialize<'de> for JwsString {
325 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
326 where
327 D: serde::Deserializer<'de>,
328 {
329 struct Visitor;
330
331 impl<'de> serde::de::Visitor<'de> for Visitor {
332 type Value = JwsString;
333
334 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
335 formatter.write_str("compact JWS")
336 }
337
338 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
339 where
340 E: serde::de::Error,
341 {
342 self.visit_string(v.to_owned())
343 }
344
345 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
346 where
347 E: serde::de::Error,
348 {
349 JwsString::from_string(v).map_err(|e| E::custom(e))
350 }
351 }
352
353 deserializer.deserialize_string(Visitor)
354 }
355}
356
357impl PartialEq<str> for JwsString {
358 fn eq(&self, other: &str) -> bool {
359 self.as_str() == other
360 }
361}
362
363impl<'a> PartialEq<&'a str> for JwsString {
364 fn eq(&self, other: &&'a str) -> bool {
365 self.as_str() == *other
366 }
367}
368
369impl PartialEq<String> for JwsString {
370 fn eq(&self, other: &String) -> bool {
371 self.as_str() == other
372 }
373}
374
375impl PartialEq<JwsString> for str {
376 fn eq(&self, other: &JwsString) -> bool {
377 self == other.as_str()
378 }
379}
380
381impl<'a> PartialEq<JwsString> for &'a str {
382 fn eq(&self, other: &JwsString) -> bool {
383 *self == other.as_str()
384 }
385}
386
387impl PartialEq<JwsString> for String {
388 fn eq(&self, other: &JwsString) -> bool {
389 self == other.as_str()
390 }
391}