1use core::str::FromStr;
2use core::convert::Infallible;
3
4use base64::{prelude::{BASE64_URL_SAFE, BASE64_URL_SAFE_NO_PAD}, DecodeError, DecodeSliceError, Engine};
5use embedded_io::{ErrorType, Read, Write};
6use hmac::{digest::Update, Hmac, Mac};
7use lil_json::{parse_json_object, serialize_json_object, JsonField, JsonObject, JsonParseFailure, JsonValue, EMPTY_FIELD};
8use sha2::{Sha256, Sha384, Sha512};
9
10use crate::{authenticated_writer::AuthenticatedWriter, base64_writer::Base64UrlBlockEncoder};
11
12mod base64_writer;
13mod authenticated_writer;
14
15struct Empty{}
16
17impl ErrorType for Empty {
18 type Error = Infallible;
19}
20
21impl Write for Empty {
22 fn write(& mut self, data: &[u8]) -> Result<usize, <Self as ErrorType>::Error> { Ok(data.len()) }
23 fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) }
24}
25
26impl Read for Empty {
27 fn read(&mut self, _: &mut [u8]) -> Result<usize, <Self as ErrorType>::Error> { Ok(0) }
28}
29
30
31#[derive(Debug,PartialEq,Eq,Clone,Copy)]
32pub enum SignatureAlgorithm {
33 HS256,
34 HS384,
35 HS512,
36}
37
38impl core::fmt::Display for SignatureAlgorithm {
39 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
40 f.write_str(Self::as_static_string(&self))
41 }
42}
43
44impl FromStr for SignatureAlgorithm {
45 type Err = ();
46 fn from_str(string: &str) -> Result<Self, <Self as FromStr>::Err> {
47 match Self::from_string(string) {
48 Some(s) => Ok(s),
49 None => Err(())
50 }
51 }
52}
53
54impl SignatureAlgorithm {
55 const fn as_static_string(&self) -> &'static str {
56 match self {
57 SignatureAlgorithm::HS256 => "HS256",
58 SignatureAlgorithm::HS384 => "HS384",
59 SignatureAlgorithm::HS512 => "HS512",
60 }
61 }
62 fn from_string(string: &str) -> Option<Self> {
63 return Some(match string {
64 "HS256" => SignatureAlgorithm::HS256,
65 "HS384" => SignatureAlgorithm::HS384,
66 "HS512" => SignatureAlgorithm::HS512,
67 _ => return None,
68 })
69 }
70}
71
72#[derive(Debug,PartialEq,Eq,Clone,Copy)]
73pub enum EncryptionAlgorithm {
74 }
76
77#[derive(Debug,PartialEq,Eq,Clone,Copy)]
78pub enum JwtType {
79 Unsecured,
80 Signed(SignatureAlgorithm),
81 Encrypted(EncryptionAlgorithm)
82}
83
84impl JwtType {
85 const fn as_static_string(&self) -> &'static str {
86 match self {
87 Self::Unsecured => "none",
88 Self::Signed(signature_algorithm) => signature_algorithm.as_static_string(),
89 Self::Encrypted(_) => todo!(),
90 }
91 }
92 fn from_string(string: &str) -> Option<Self> {
93 if string == "none" {
94 return Some(Self::Unsecured);
95 } else if let Some(s) = SignatureAlgorithm::from_string(string) {
96 return Some(JwtType::Signed(s))
97 }
98 return None;
100 }
101}
102
103#[derive(Debug)]
104pub enum JwtParseFailure {
105 NotEnoughDots,
106 InvalidBase64Url(DecodeError),
107 Base64BufferTooSmall,
108 InvalidSignature,
109 AlgorithmMismatch,
111 IncorrectHeader,
112 InvalidHeader(JsonParseFailure),
113 InvalidClaims(JsonParseFailure),
114}
115
116pub struct JsonWebToken<'a> {
117 claims: &'a [JsonField<'a,'a>],
118}
119
120impl<'a> JsonWebToken<'a> {
121
122 pub fn from_claims(claims: &'a [JsonField<'a,'a>]) -> Self {
123 Self { claims }
124 }
125
126 pub fn serialize<T: Write>(&self, output: T, algorithm: JwtType, secret: &[u8]) -> Result<usize,T::Error> {
127 serialize_jwt(output, self.claims, &algorithm, secret)
128 }
129
130 pub fn deserialize_claims<const MAX_CLAIMS: usize>(data: &'a[u8], base64buffer: &'a mut [u8], algorithm: JwtType, secret: &[u8]) -> Result<JsonObject<'a,MAX_CLAIMS>,JwtParseFailure> {
131 let mut claims_buffer = [EMPTY_FIELD; MAX_CLAIMS];
132 let num_claims = deserialize_jwt(data, &mut claims_buffer, &algorithm, secret, base64buffer)?;
133 let mut ret = JsonObject::<MAX_CLAIMS>::new();
134 for claim in claims_buffer.split_at(num_claims).0 {
135 ret.push(*claim).expect("ret holds MAX_CLAIMS");
136 }
137 Ok(ret)
138 }
139}
140
141const fn get_jose_header(include_typ_header: bool, algorithm: &JwtType) -> JsonObject<'static,2> {
142 let mut ret = JsonObject::<2>::new();
143 match ret.push_field("alg", JsonValue::String(algorithm.as_static_string())) {
144 Ok(()) => {},
145 Err(()) => unreachable!(),
146 }
147 if include_typ_header {
148 match ret.push_field("typ", JsonValue::String("JWT")) {
149 Ok(()) => {},
150 Err(()) => unreachable!(),
151 }
152 }
153 ret
154}
155
156fn split_jwt_parts(data: &[u8]) -> Result<(&[u8],&[u8],&[u8]),JwtParseFailure> {
157 let mut dot_indices = data
158 .iter()
159 .enumerate()
160 .filter_map(|(i, &b)| if b == b'.' { Some(i) } else { None });
161 let first_dot = match dot_indices.next() {
162 Some(f) => f,
163 None => return Err(JwtParseFailure::NotEnoughDots),
164 };
165 let second_dot = match dot_indices.next() {
166 Some(s) => s,
167 None => return Err(JwtParseFailure::NotEnoughDots),
168 };
169 let (header_slice, after_header) = data.split_at(first_dot);
170 let (body_with_dot, signature_with_dot) = after_header.split_at(second_dot - first_dot);
171 let body_slice = body_with_dot.split_at(1).1;
172 let signature_slice = signature_with_dot.split_at(1).1;
173 Ok((header_slice,body_slice,signature_slice))
174}
175
176fn verify_jose_header(header_fields: &[JsonField<'_,'_>], expected_algorithm: &JwtType) -> Result<(),JwtParseFailure> {
177 let mut alg_header: Option<&str> = None;
178 for header_field in header_fields {
179 if header_field.key == "alg" {
180 match header_field.value {
181 JsonValue::String(alg_value) => {
182 match alg_header.replace(alg_value) {
183 None => {},
184 Some(_duplicate_alg_header) => return Err(JwtParseFailure::IncorrectHeader),
185 }
186 },
187 _ => return Err(JwtParseFailure::IncorrectHeader)
188 }
189 }
190 }
191 let alg_header_value = match alg_header {
192 None => return Err(JwtParseFailure::IncorrectHeader),
193 Some(v) => v,
194 };
195 if alg_header_value != expected_algorithm.as_static_string() {
196 return Err(JwtParseFailure::AlgorithmMismatch);
197 }
198 Ok(())
199}
200
201pub fn deserialize_jwt<'a>(data: &'a [u8], claims_buffer: &mut [JsonField<'a,'a>], algorithm: &JwtType, secret: &[u8], base64buffer: &'a mut [u8]) -> Result<usize,JwtParseFailure> {
202 let (header_b64,body_b64,signature_b64) = split_jwt_parts(data)?;
203 match algorithm {
204 JwtType::Unsecured => {
205 let header_decoded_end = match BASE64_URL_SAFE_NO_PAD.decode_slice(header_b64, base64buffer) {
206 Ok(n) => n,
207 Err(DecodeSliceError::OutputSliceTooSmall) => return Err(JwtParseFailure::Base64BufferTooSmall),
208 Err(DecodeSliceError::DecodeError(e)) => return Err(JwtParseFailure::InvalidBase64Url(e)),
209 };
210 let (decoded_header,remaining_base64_buffer) = base64buffer.split_at_mut(header_decoded_end);
211 let body_decoded_end = match BASE64_URL_SAFE_NO_PAD.decode_slice(body_b64, remaining_base64_buffer) {
212 Ok(n) => n,
213 Err(DecodeSliceError::OutputSliceTooSmall) => return Err(JwtParseFailure::Base64BufferTooSmall),
214 Err(DecodeSliceError::DecodeError(e)) => return Err(JwtParseFailure::InvalidBase64Url(e)),
215 };
216 let decoded_claims = remaining_base64_buffer.split_at(body_decoded_end).0;
217 let mut header_buffer = [EMPTY_FIELD; 5];
218 let (_num_data,num_header_fields) = match parse_json_object(decoded_header, &mut header_buffer) {
219 Ok(n) => n,
220 Err(j) => return Err(JwtParseFailure::InvalidHeader(j)),
221 };
222 verify_jose_header(header_buffer.split_at(num_header_fields).0, algorithm)?;
223 let num_claims = match parse_json_object(decoded_claims, claims_buffer) {
224 Ok((_num_bytes,n)) => n,
225 Err(j) => return Err(JwtParseFailure::InvalidClaims(j)),
226 };
227 Ok(num_claims)
228 },
229 JwtType::Signed(SignatureAlgorithm::HS256) => {
230 let digest = Hmac::<Sha256>::new_from_slice(secret).expect("invalid HS256 secret");
231 let mac = digest
232 .chain_update(header_b64)
233 .chain_update(b".")
234 .chain_update(body_b64)
235 .finalize()
236 .into_bytes();
237 let signature_decoded_end = match BASE64_URL_SAFE_NO_PAD.decode_slice(signature_b64, base64buffer) {
238 Ok(n) => n,
239 Err(DecodeSliceError::OutputSliceTooSmall) => return Err(JwtParseFailure::Base64BufferTooSmall),
240 Err(DecodeSliceError::DecodeError(e)) => return Err(JwtParseFailure::InvalidBase64Url(e)),
241 };
242 if signature_decoded_end != 32 || mac.as_slice() != base64buffer.split_at(signature_decoded_end).0 {
243 return Err(JwtParseFailure::InvalidSignature);
244 }
245 let header_decoded_end = match BASE64_URL_SAFE_NO_PAD.decode_slice(header_b64, base64buffer) {
246 Ok(n) => n,
247 Err(DecodeSliceError::OutputSliceTooSmall) => return Err(JwtParseFailure::Base64BufferTooSmall),
248 Err(DecodeSliceError::DecodeError(e)) => return Err(JwtParseFailure::InvalidBase64Url(e)),
249 };
250 let (decoded_header,remaining_base64_buffer) = base64buffer.split_at_mut(header_decoded_end);
251 let body_decoded_end = match BASE64_URL_SAFE_NO_PAD.decode_slice(body_b64, remaining_base64_buffer) {
252 Ok(n) => n,
253 Err(DecodeSliceError::OutputSliceTooSmall) => return Err(JwtParseFailure::Base64BufferTooSmall),
254 Err(DecodeSliceError::DecodeError(e)) => return Err(JwtParseFailure::InvalidBase64Url(e)),
255 };
256 let decoded_claims = remaining_base64_buffer.split_at(body_decoded_end).0;
257 let mut header_buffer = [EMPTY_FIELD; 5];
258 let (_num_data,num_header_fields) = match parse_json_object(decoded_header, &mut header_buffer) {
259 Ok(n) => n,
260 Err(j) => return Err(JwtParseFailure::InvalidHeader(j)),
261 };
262 verify_jose_header(header_buffer.split_at(num_header_fields).0, algorithm)?;
263 let num_claims = match parse_json_object(decoded_claims, claims_buffer) {
264 Ok((_num_bytes,n)) => n,
265 Err(j) => return Err(JwtParseFailure::InvalidClaims(j)),
266 };
267 Ok(num_claims)
268 },
269 JwtType::Signed(SignatureAlgorithm::HS384) => {
270 let digest = Hmac::<Sha384>::new_from_slice(secret).expect("invalid HS256 secret");
271 let mac = digest
272 .chain_update(header_b64)
273 .chain_update(b".")
274 .chain_update(body_b64)
275 .finalize()
276 .into_bytes();
277 let signature_decoded_end = match BASE64_URL_SAFE_NO_PAD.decode_slice(signature_b64, base64buffer) {
278 Ok(n) => n,
279 Err(DecodeSliceError::OutputSliceTooSmall) => return Err(JwtParseFailure::Base64BufferTooSmall),
280 Err(DecodeSliceError::DecodeError(e)) => return Err(JwtParseFailure::InvalidBase64Url(e)),
281 };
282 if signature_decoded_end != 48 || mac.as_slice() != base64buffer.split_at(signature_decoded_end).0 {
283 return Err(JwtParseFailure::InvalidSignature);
284 }
285 todo!()
286 },
287 JwtType::Signed(SignatureAlgorithm::HS512) => {
288 let digest = Hmac::<Sha512>::new_from_slice(secret).expect("invalid HS256 secret");
289 let mac = digest
290 .chain_update(header_b64)
291 .chain_update(b".")
292 .chain_update(body_b64)
293 .finalize()
294 .into_bytes();
295 let signature_decoded_end = match BASE64_URL_SAFE_NO_PAD.decode_slice(signature_b64, base64buffer) {
296 Ok(n) => n,
297 Err(DecodeSliceError::OutputSliceTooSmall) => return Err(JwtParseFailure::Base64BufferTooSmall),
298 Err(DecodeSliceError::DecodeError(e)) => return Err(JwtParseFailure::InvalidBase64Url(e)),
299 };
300 if signature_decoded_end != 64 || mac.as_slice() != base64buffer.split_at(signature_decoded_end).0 {
301 return Err(JwtParseFailure::InvalidSignature);
302 }
303 todo!()
304 },
305 _ => todo!()
306 }
307}
308
309fn serialize_object_base64<T: embedded_io::Write>(output: T, claims: &[JsonField<'_,'_>]) -> Result<usize,T::Error> {
310 let mut body_encoder = Base64UrlBlockEncoder::new(output);
311 serialize_json_object(&mut body_encoder, claims)?;
312 body_encoder.finalize(false)
313}
314
315fn serialize_slice_base64<T: embedded_io::Write>(output: T, slice: &[u8]) -> Result<usize,T::Error> {
316 let mut slice_encoder = Base64UrlBlockEncoder::new(output);
317 slice_encoder.write_all(slice)?;
318 slice_encoder.finalize(false)
319}
320
321pub fn serialize_jwt<T: embedded_io::Write>(mut output: T, claims: &[JsonField<'_,'_>], algorithm: &JwtType, secret: &[u8]) -> Result<usize,T::Error> {
322 let header = get_jose_header(algorithm != &JwtType::Unsecured, algorithm);
323 let mut ret = 0;
324 match algorithm {
325 JwtType::Unsecured => {
326 ret += serialize_object_base64(&mut output, header.as_slice())?;
327 output.write_all(b".")?;
328 ret += 1;
329 ret += serialize_object_base64(&mut output, claims)?;
330 output.write_all(b".")?;
331 ret += 1;
332 Ok(ret)
333 },
334 JwtType::Signed(SignatureAlgorithm::HS256) => {
335 let digest = Hmac::<Sha256>::new_from_slice(secret).expect("invalid HS256 secret");
337 let mut authenticated_writer = AuthenticatedWriter::new(&mut output, digest);
338 ret += serialize_object_base64(&mut authenticated_writer, header.as_slice()).unwrap();
339 authenticated_writer.write_all(b".")?;
340 ret += 1;
341 ret += serialize_object_base64(&mut authenticated_writer, claims)?;
342 let mac = authenticated_writer.finalize_mac();
343 output.write_all(b".")?;
344 ret += 1;
345 ret += serialize_slice_base64(&mut output, &mac.into_bytes())?;
346 Ok(ret)
347 },
348 JwtType::Signed(SignatureAlgorithm::HS384) => {
349 let digest = Hmac::<Sha384>::new_from_slice(secret).expect("invalid HS384 secret");
351 let mut authenticated_writer = AuthenticatedWriter::new(&mut output, digest);
352 ret += serialize_object_base64(&mut authenticated_writer, header.as_slice()).unwrap();
353 authenticated_writer.write_all(b".")?;
354 ret += 1;
355 ret += serialize_object_base64(&mut authenticated_writer, claims)?;
356 let mac = authenticated_writer.finalize_mac();
357 output.write_all(b".")?;
358 ret += 1;
359 ret += serialize_slice_base64(&mut output, &mac.into_bytes())?;
360 Ok(ret)
361 },
362 JwtType::Signed(SignatureAlgorithm::HS512) => {
363 let digest = Hmac::<Sha512>::new_from_slice(secret).expect("invalid HS512 secret");
365 let mut authenticated_writer = AuthenticatedWriter::new(&mut output, digest);
366 ret += serialize_object_base64(&mut authenticated_writer, header.as_slice()).unwrap();
367 authenticated_writer.write_all(b".")?;
368 ret += 1;
369 ret += serialize_object_base64(&mut authenticated_writer, claims)?;
370 let mac = authenticated_writer.finalize_mac();
371 output.write_all(b".")?;
372 ret += 1;
373 ret += serialize_slice_base64(&mut output, &mac.into_bytes())?;
374 Ok(ret)
375 },
376 JwtType::Encrypted(_) => todo!(),
377 }
378}
379
380
381#[cfg(test)]
382mod tests {
383 use super::*;
384
385 #[test]
386 fn test_serialize_unsecured_empty() {
387 let mut buffer = [0_u8; 256];
388 let n = JsonWebToken::from_claims(&[]).serialize(buffer.as_mut_slice(), JwtType::Unsecured, &[]).unwrap();
389 assert_eq!(b"eyJhbGciOiJub25lIn0.e30.", buffer.split_at(n).0)
390 }
391
392}