1use b64_url::{
2 B64Config, B64ConfigPadding, _b64_url_encode_calculate_destination_capacity,
3 _b64_url_encode_with_config,
4};
5use bytes::Bytes;
6use lazy_static::lazy_static;
7
8pub mod header;
9pub mod payload;
10pub mod signer;
11
12use header::Header;
13use payload::Payload;
14use signer::Signer;
15
16lazy_static! {
17 static ref B64_CONFIG: B64Config = B64Config {
18 padding: B64ConfigPadding { omit: true }
19 };
20}
21
22#[derive(Debug)]
23pub struct Jwt {
24 pub bytes: Bytes,
25 pub header: Header,
26 pub header_bytes: Bytes,
27 pub payload: Payload,
28 pub payload_bytes: Bytes,
29 pub signature: Vec<u8>,
30 pub signature_bytes: Bytes,
31}
32
33#[derive(Debug, Clone)]
34pub struct JwtBuilder {
35 pub payload: Payload,
36}
37
38impl JwtBuilder {
39 pub fn build(self, signer: &Signer) -> Jwt {
40 let signer = signer.clone();
41
42 let header = signer.header();
43 let header_json = serde_json::to_string(&header).unwrap();
44 let payload_json = serde_json::to_string(&self.payload).unwrap();
45
46 let mut buffer = Vec::<u8>::with_capacity(
47 _b64_url_encode_calculate_destination_capacity(header_json.len())
48 + _b64_url_encode_calculate_destination_capacity(payload_json.len())
49 + _b64_url_encode_calculate_destination_capacity(signer.signature_length())
50 + 2,
51 );
52
53 let header_num_bytes;
54 let payload_num_bytes;
55 let signature_num_bytes;
56 let mut destination_ptr = buffer.as_mut_ptr();
57 unsafe {
58 header_num_bytes = _b64_url_encode_with_config(
59 header_json.as_ptr(),
60 header_json.len(),
61 destination_ptr,
62 &B64_CONFIG,
63 );
64 destination_ptr = destination_ptr.add(header_num_bytes + 1);
65 payload_num_bytes = _b64_url_encode_with_config(
66 payload_json.as_ptr(),
67 payload_json.len(),
68 destination_ptr,
69 &B64_CONFIG,
70 );
71 buffer.set_len(header_num_bytes + payload_num_bytes + 1);
72 }
73 buffer[header_num_bytes] = 0x2e;
74
75 let signature = signer.signature(buffer.as_slice());
76 unsafe {
77 destination_ptr = destination_ptr.add(payload_num_bytes + 1);
78 signature_num_bytes = _b64_url_encode_with_config(
79 signature.as_ptr(),
80 signature.len(),
81 destination_ptr,
82 &B64_CONFIG,
83 );
84 buffer.set_len(header_num_bytes + payload_num_bytes + signature_num_bytes + 2);
85 }
86 buffer[header_num_bytes + payload_num_bytes + 1] = 0x2e;
87
88 let bytes = Bytes::from(buffer);
89 let header_bytes = bytes.slice(0..header_num_bytes);
90 let payload_bytes =
91 bytes.slice((header_num_bytes + 1)..(header_num_bytes + payload_num_bytes + 1));
92 let signature_bytes = bytes.slice(
93 (header_num_bytes + payload_num_bytes + 2)
94 ..(header_num_bytes + payload_num_bytes + signature_num_bytes + 2),
95 );
96
97 Jwt {
98 bytes,
99 header,
100 header_bytes,
101 payload: self.payload,
102 payload_bytes,
103 signature,
104 signature_bytes,
105 }
106 }
107}
108
109#[cfg(test)]
110mod jwt_builder_tests {
111 use super::*;
112 use bytes::Bytes;
113 use hmac::{Hmac, Mac};
114 use sha2::Sha256;
115
116 #[test]
117 fn new() {
118 let mut payload = Payload::new();
119 payload.issuer = Some(String::from("hello").into());
120 payload.subject = Some(true.into());
121
122 let jwt_builder = JwtBuilder { payload };
123
124 let signer =
125 Signer::HmacSha256(Hmac::<Sha256>::new_from_slice(b"your-256-bit-secret").unwrap());
126
127 let jwt = jwt_builder.build(&signer);
128
129 assert_eq!(jwt.bytes, Bytes::from("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJoZWxsbyIsInN1YiI6dHJ1ZX0.Xk_NUrp8IZ4mvrATTB67AlpBmWDTufz6JHFpz_13KZg"));
130 }
131}