1#![cfg_attr(not(feature = "std"), no_std)]
2#![deny(unsafe_code)]
3#![deny(missing_docs)]
4#![deny(clippy::all)]
5#![deny(clippy::pedantic)]
6
7#[cfg(feature = "alloc")]
24extern crate alloc;
25
26#[cfg(feature = "alloc")]
27use alloc::{string::String, vec::Vec};
28
29#[cfg(feature = "alloc")]
30use base64_ng::{
31 Alphabet, DecodeError, Engine, MIME, PEM, Profile, STANDARD, STANDARD_NO_PAD, URL_SAFE,
32 URL_SAFE_NO_PAD, clear_bytes, constant_time_eq,
33};
34#[cfg(feature = "alloc")]
35use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
36
37#[cfg(feature = "alloc")]
44#[derive(Clone)]
45pub struct Base64Standard(Vec<u8>);
46
47#[cfg(feature = "alloc")]
54#[derive(Clone)]
55pub struct Base64UrlSafeNoPad(Vec<u8>);
56
57#[cfg(feature = "alloc")]
58impl Base64Standard {
59 #[must_use]
61 pub const fn new(bytes: Vec<u8>) -> Self {
62 Self(bytes)
63 }
64
65 #[must_use]
67 pub fn as_bytes(&self) -> &[u8] {
68 &self.0
69 }
70
71 #[must_use]
76 pub fn into_inner(mut self) -> Vec<u8> {
77 core::mem::take(&mut self.0)
78 }
79}
80
81#[cfg(feature = "alloc")]
82impl Base64UrlSafeNoPad {
83 #[must_use]
85 pub const fn new(bytes: Vec<u8>) -> Self {
86 Self(bytes)
87 }
88
89 #[must_use]
91 pub fn as_bytes(&self) -> &[u8] {
92 &self.0
93 }
94
95 #[must_use]
100 pub fn into_inner(mut self) -> Vec<u8> {
101 core::mem::take(&mut self.0)
102 }
103}
104
105#[cfg(feature = "alloc")]
106impl Drop for Base64Standard {
107 fn drop(&mut self) {
108 clear_bytes(&mut self.0);
109 }
110}
111
112#[cfg(feature = "alloc")]
113impl Drop for Base64UrlSafeNoPad {
114 fn drop(&mut self) {
115 clear_bytes(&mut self.0);
116 }
117}
118
119#[cfg(feature = "alloc")]
120impl PartialEq for Base64Standard {
121 fn eq(&self, other: &Self) -> bool {
122 constant_time_eq(self.as_bytes(), other.as_bytes())
123 }
124}
125
126#[cfg(feature = "alloc")]
127impl Eq for Base64Standard {}
128
129#[cfg(feature = "alloc")]
130impl PartialEq for Base64UrlSafeNoPad {
131 fn eq(&self, other: &Self) -> bool {
132 constant_time_eq(self.as_bytes(), other.as_bytes())
133 }
134}
135
136#[cfg(feature = "alloc")]
137impl Eq for Base64UrlSafeNoPad {}
138
139#[cfg(feature = "alloc")]
140impl core::fmt::Debug for Base64Standard {
141 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
142 formatter
143 .debug_struct("Base64Standard")
144 .field("bytes", &"<redacted>")
145 .field("len", &self.0.len())
146 .finish()
147 }
148}
149
150#[cfg(feature = "alloc")]
151impl core::fmt::Debug for Base64UrlSafeNoPad {
152 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
153 formatter
154 .debug_struct("Base64UrlSafeNoPad")
155 .field("bytes", &"<redacted>")
156 .field("len", &self.0.len())
157 .finish()
158 }
159}
160
161#[cfg(feature = "alloc")]
162impl Serialize for Base64Standard {
163 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164 where
165 S: Serializer,
166 {
167 standard::serialize(&self.0, serializer)
168 }
169}
170
171#[cfg(feature = "alloc")]
172impl<'de> Deserialize<'de> for Base64Standard {
173 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
174 where
175 D: Deserializer<'de>,
176 {
177 standard::deserialize(deserializer).map(Self)
178 }
179}
180
181#[cfg(feature = "alloc")]
182impl Serialize for Base64UrlSafeNoPad {
183 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
184 where
185 S: Serializer,
186 {
187 url_safe_no_pad::serialize(&self.0, serializer)
188 }
189}
190
191#[cfg(feature = "alloc")]
192impl<'de> Deserialize<'de> for Base64UrlSafeNoPad {
193 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
194 where
195 D: Deserializer<'de>,
196 {
197 url_safe_no_pad::deserialize(deserializer).map(Self)
198 }
199}
200
201#[cfg(feature = "alloc")]
209pub mod standard {
210 use super::{STANDARD, Vec, deserialize_with_engine, serialize_with_engine};
211 use serde::{Deserializer, Serializer};
212
213 pub fn serialize<S>(bytes: impl AsRef<[u8]>, serializer: S) -> Result<S::Ok, S::Error>
220 where
221 S: Serializer,
222 {
223 serialize_with_engine(STANDARD, bytes.as_ref(), serializer)
224 }
225
226 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
233 where
234 D: Deserializer<'de>,
235 {
236 deserialize_with_engine(STANDARD, deserializer)
237 }
238}
239
240#[cfg(feature = "alloc")]
248pub mod standard_no_pad {
249 use super::{STANDARD_NO_PAD, Vec, deserialize_with_engine, serialize_with_engine};
250 use serde::{Deserializer, Serializer};
251
252 pub fn serialize<S>(bytes: impl AsRef<[u8]>, serializer: S) -> Result<S::Ok, S::Error>
259 where
260 S: Serializer,
261 {
262 serialize_with_engine(STANDARD_NO_PAD, bytes.as_ref(), serializer)
263 }
264
265 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
272 where
273 D: Deserializer<'de>,
274 {
275 deserialize_with_engine(STANDARD_NO_PAD, deserializer)
276 }
277}
278
279#[cfg(feature = "alloc")]
287pub mod url_safe {
288 use super::{URL_SAFE, Vec, deserialize_with_engine, serialize_with_engine};
289 use serde::{Deserializer, Serializer};
290
291 pub fn serialize<S>(bytes: impl AsRef<[u8]>, serializer: S) -> Result<S::Ok, S::Error>
298 where
299 S: Serializer,
300 {
301 serialize_with_engine(URL_SAFE, bytes.as_ref(), serializer)
302 }
303
304 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
311 where
312 D: Deserializer<'de>,
313 {
314 deserialize_with_engine(URL_SAFE, deserializer)
315 }
316}
317
318#[cfg(feature = "alloc")]
326pub mod url_safe_no_pad {
327 use super::{URL_SAFE_NO_PAD, Vec, deserialize_with_engine, serialize_with_engine};
328 use serde::{Deserializer, Serializer};
329
330 pub fn serialize<S>(bytes: impl AsRef<[u8]>, serializer: S) -> Result<S::Ok, S::Error>
337 where
338 S: Serializer,
339 {
340 serialize_with_engine(URL_SAFE_NO_PAD, bytes.as_ref(), serializer)
341 }
342
343 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
350 where
351 D: Deserializer<'de>,
352 {
353 deserialize_with_engine(URL_SAFE_NO_PAD, deserializer)
354 }
355}
356
357#[cfg(feature = "alloc")]
365pub mod mime {
366 use super::{MIME, Vec, deserialize_with_profile, serialize_with_profile};
367 use serde::{Deserializer, Serializer};
368
369 pub fn serialize<S>(bytes: impl AsRef<[u8]>, serializer: S) -> Result<S::Ok, S::Error>
376 where
377 S: Serializer,
378 {
379 serialize_with_profile(&MIME, bytes.as_ref(), serializer)
380 }
381
382 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
390 where
391 D: Deserializer<'de>,
392 {
393 deserialize_with_profile(&MIME, deserializer)
394 }
395}
396
397#[cfg(feature = "alloc")]
405pub mod pem {
406 use super::{PEM, Vec, deserialize_with_profile, serialize_with_profile};
407 use serde::{Deserializer, Serializer};
408
409 pub fn serialize<S>(bytes: impl AsRef<[u8]>, serializer: S) -> Result<S::Ok, S::Error>
416 where
417 S: Serializer,
418 {
419 serialize_with_profile(&PEM, bytes.as_ref(), serializer)
420 }
421
422 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
430 where
431 D: Deserializer<'de>,
432 {
433 deserialize_with_profile(&PEM, deserializer)
434 }
435}
436
437#[cfg(feature = "alloc")]
438fn serialize_with_engine<A, const PAD: bool, S>(
439 engine: Engine<A, PAD>,
440 bytes: &[u8],
441 serializer: S,
442) -> Result<S::Ok, S::Error>
443where
444 A: base64_ng::Alphabet,
445 S: Serializer,
446{
447 let encoded = engine
448 .encode_string(bytes)
449 .map_err(serde::ser::Error::custom)?;
450 serializer.serialize_str(&encoded)
451}
452
453#[cfg(feature = "alloc")]
454fn serialize_with_profile<A, const PAD: bool, S>(
455 profile: &Profile<A, PAD>,
456 bytes: &[u8],
457 serializer: S,
458) -> Result<S::Ok, S::Error>
459where
460 A: Alphabet,
461 S: Serializer,
462{
463 let encoded = profile
464 .encode_string(bytes)
465 .map_err(serde::ser::Error::custom)?;
466 serializer.serialize_str(&encoded)
467}
468
469#[cfg(feature = "alloc")]
470fn deserialize_with_engine<'de, A, const PAD: bool, D>(
471 engine: Engine<A, PAD>,
472 deserializer: D,
473) -> Result<Vec<u8>, D::Error>
474where
475 A: base64_ng::Alphabet,
476 D: Deserializer<'de>,
477{
478 let encoded = String::deserialize(deserializer)?;
479 engine
480 .decode_vec(encoded.as_bytes())
481 .map_err(|error: DecodeError| D::Error::custom(error.kind()))
482}
483
484#[cfg(feature = "alloc")]
485fn deserialize_with_profile<'de, A, const PAD: bool, D>(
486 profile: &Profile<A, PAD>,
487 deserializer: D,
488) -> Result<Vec<u8>, D::Error>
489where
490 A: Alphabet,
491 D: Deserializer<'de>,
492{
493 let encoded = String::deserialize(deserializer)?;
494 profile
495 .decode_vec(encoded.as_bytes())
496 .map_err(|error: DecodeError| D::Error::custom(error.kind()))
497}