Skip to main content

json_web_tolkien/
jws.rs

1use base64ct::{Base64UrlUnpadded, Encoding};
2use serde::{
3    Deserialize, Serialize,
4    de::{self, IntoDeserializer, value::SeqDeserializer},
5};
6use std::{borrow::Cow, fmt};
7
8mod encoded_token;
9mod signature;
10
11use encoded_token::EncodedToken;
12use signature::Signature;
13
14use crate::error::Error;
15use crate::ser::Serializer;
16
17/// JSON Web Signature.
18#[cfg_attr(feature = "debug", derive(Debug))]
19pub struct Jws {
20    encoded_token: EncodedToken,
21    signature: Signature,
22}
23
24impl Jws {
25    #[cfg_attr(feature = "debug", tracing::instrument)]
26    pub fn new(encoded_token: &str, signature: &[u8]) -> Self {
27        let encoded_token = EncodedToken::from(encoded_token);
28        let signature = Signature::from(signature);
29
30        Self {
31            encoded_token,
32            signature,
33        }
34    }
35
36    /// Returns the base64 encoded header and claims.
37    /// This can be deserialized futher into a jwt or
38    /// used to verify the signature.
39    pub fn encoded_token(&self) -> Cow<'_, str> {
40        let ptr: &str = &self.encoded_token;
41
42        Cow::from(ptr)
43    }
44
45    /// The decoded signature.
46    pub fn signature(&self) -> Cow<'_, [u8]> {
47        let ptr: &[u8] = self.signature.as_ref();
48
49        Cow::from(ptr)
50    }
51
52    pub fn to_string(&self) -> Result<String, Error> {
53        let mut serializer = Serializer {
54            output: String::new(),
55        };
56
57        self.serialize(&mut serializer)?;
58
59        Ok(serializer.output)
60    }
61}
62
63impl<'de> Deserialize<'de> for Jws {
64    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
65    where
66        D: de::Deserializer<'de>,
67    {
68        struct Visitor;
69
70        impl<'de> de::Visitor<'de> for Visitor {
71            type Value = Jws;
72
73            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
74                write!(
75                    f,
76                    "<header (json via base64)>.<claims (json via base64)>.<signature (base64)>"
77                )
78            }
79
80            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
81            where
82                A: de::SeqAccess<'de>,
83            {
84                let signature: Signature = seq
85                    .next_element()?
86                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
87
88                let encoded_token: EncodedToken = seq
89                    .next_element()?
90                    .ok_or_else(|| de::Error::invalid_length(1, &self))?;
91
92                Ok(Self::Value {
93                    encoded_token,
94                    signature,
95                })
96            }
97
98            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
99            where
100                E: de::Error,
101            {
102                let parts = v.rsplitn(2, '.');
103
104                let de = SeqDeserializer::new(parts);
105
106                self.visit_seq(de)
107            }
108        }
109
110        deserializer.deserialize_str(Visitor)
111    }
112}
113
114impl Serialize for Jws {
115    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
116    where
117        S: serde::Serializer,
118    {
119        let enc_token: Cow<str> = self.encoded_token();
120
121        let signature: String = {
122            let signature: Cow<[u8]> = self.signature();
123
124            Base64UrlUnpadded::encode_string(&signature)
125        };
126
127        serializer.collect_str(&format_args!("{}.{}", enc_token, signature))
128    }
129}
130
131impl TryFrom<&str> for Jws {
132    type Error = Error;
133
134    fn try_from(value: &str) -> Result<Self, Self::Error> {
135        let de = value.into_deserializer();
136
137        Self::deserialize(de)
138    }
139}
140
141#[cfg(test)]
142mod test {
143    use crate::prelude::*;
144    use hmac::Mac;
145    use serde::{Deserialize, Serialize};
146    use serde_json::json;
147    use sha2;
148
149    type Hmac256 = hmac::Hmac<sha2::Sha256>;
150
151    #[derive(Deserialize, Serialize)]
152    struct TestData {
153        pub token: Jws,
154    }
155
156    #[test]
157    fn test_from_str() {
158        let valid_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30";
159
160        let input = json!({
161            "token": valid_token
162        })
163        .to_string();
164
165        let data: TestData = serde_json::from_str(&input).unwrap();
166        let jws: &Jws = &data.token;
167
168        assert_eq!(input, serde_json::to_string(&data).unwrap());
169
170        assert_eq!(
171            "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0".as_bytes(),
172            jws.encoded_token().as_bytes()
173        );
174
175        assert_eq!(
176            [
177                40, 197, 5, 176, 128, 211, 156, 89, 178, 27, 121, 204, 136, 99, 58, 31, 209, 77,
178                21, 68, 78, 127, 124, 33, 237, 41, 170, 38, 159, 144, 87, 125
179            ],
180            jws.signature().as_ref()
181        );
182    }
183
184    #[test]
185    fn test_signature_validation() {
186        let signed_message = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30";
187
188        let secret = "a-string-secret-at-least-256-bits-long";
189
190        let input = json!({
191            "token": signed_message
192        })
193        .to_string();
194
195        let data: TestData = serde_json::from_str(&input).unwrap();
196        let jws: Jws = data.token;
197
198        let mut mac = Hmac256::new_from_slice(secret.as_bytes()).unwrap();
199
200        mac.update(jws.encoded_token().as_bytes());
201
202        assert!(mac.verify_slice(&jws.signature()).is_ok())
203    }
204}