credential_exchange_format/
b64url.rs

1use data_encoding::{Specification, BASE32_NOPAD, BASE64URL, BASE64URL_NOPAD};
2use serde::{Deserialize, Serialize};
3
4/// Base64URL encoded data
5#[derive(Debug, Serialize, Deserialize, Clone, Hash, PartialEq, Eq)]
6#[serde(try_from = "&str", into = "String")]
7pub struct B64Url(Vec<u8>);
8
9impl From<Vec<u8>> for B64Url {
10    fn from(src: Vec<u8>) -> Self {
11        Self(src)
12    }
13}
14impl From<&[u8]> for B64Url {
15    fn from(src: &[u8]) -> Self {
16        Self(src.to_vec())
17    }
18}
19
20impl From<B64Url> for Vec<u8> {
21    fn from(src: B64Url) -> Self {
22        src.0
23    }
24}
25
26impl AsRef<[u8]> for B64Url {
27    fn as_ref(&self) -> &[u8] {
28        &self.0
29    }
30}
31
32impl From<B64Url> for String {
33    fn from(src: B64Url) -> Self {
34        String::from(&src)
35    }
36}
37impl From<&B64Url> for String {
38    fn from(src: &B64Url) -> Self {
39        BASE64URL_NOPAD.encode(&src.0)
40    }
41}
42
43impl std::fmt::Display for B64Url {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        f.write_str(String::from(self).as_str())
46    }
47}
48
49/// An error returned when a string is not base64 decodable.
50#[derive(Debug)]
51pub struct NotB64UrlEncoded;
52
53impl std::fmt::Display for NotB64UrlEncoded {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        f.write_str("Data isn't base64url encoded")
56    }
57}
58
59impl TryFrom<&str> for B64Url {
60    type Error = NotB64UrlEncoded;
61
62    fn try_from(value: &str) -> Result<Self, Self::Error> {
63        let specs = BASE64URL.specification();
64        let padding = specs.padding.unwrap();
65        let specs = Specification {
66            check_trailing_bits: false,
67            padding: None,
68            ..specs
69        };
70        let encoding = specs.encoding().unwrap();
71        let sane_string = value.trim_end_matches(padding);
72        encoding
73            .decode(sane_string.as_bytes())
74            .map(Self)
75            .map_err(|_| NotB64UrlEncoded)
76    }
77}
78
79/// Newtype to encode and decode a vector of bytes to and from Base32.
80#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
81#[serde(try_from = "&str", into = "String")]
82pub struct B32(Vec<u8>);
83
84impl From<Vec<u8>> for B32 {
85    fn from(src: Vec<u8>) -> Self {
86        Self(src)
87    }
88}
89impl From<&[u8]> for B32 {
90    fn from(src: &[u8]) -> Self {
91        Self(src.to_vec())
92    }
93}
94
95impl From<B32> for Vec<u8> {
96    fn from(src: B32) -> Self {
97        src.0
98    }
99}
100
101impl AsRef<[u8]> for B32 {
102    fn as_ref(&self) -> &[u8] {
103        &self.0
104    }
105}
106
107impl From<B32> for String {
108    fn from(src: B32) -> Self {
109        BASE32_NOPAD.encode(&src.0)
110    }
111}
112
113/// The string was not base32 encoded
114#[derive(Debug)]
115pub struct NotBase32Encoded;
116
117impl std::fmt::Display for NotBase32Encoded {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        f.write_str("Data isn't base32 encoded")
120    }
121}
122
123impl TryFrom<&str> for B32 {
124    type Error = NotBase32Encoded;
125
126    fn try_from(value: &str) -> Result<Self, Self::Error> {
127        let symbols = BASE32_NOPAD.specification().symbols;
128        let mut sane_string: String = value.to_ascii_uppercase();
129        sane_string.retain(|c| symbols.contains(c));
130        BASE32_NOPAD
131            .decode(sane_string.as_bytes())
132            .map(Self)
133            .map_err(|_| NotBase32Encoded)
134    }
135}