Skip to main content

pdk_jwt_lib/generator/
mod.rs

1// Copyright (c) 2026, Salesforce, Inc.,
2// All rights reserved.
3// For full license text, see the LICENSE.txt file
4use crate::api::model::{SigningAlgorithm, SigningKeyLength};
5use crate::model::claims::JWTClaims;
6use jwt_simple::algorithms::{
7    ECDSAP256KeyPairLike, ECDSAP384KeyPairLike, ES256KeyPair, ES384KeyPair, HS256Key, HS384Key,
8    HS512Key, MACLike, RS256KeyPair, RS384KeyPair, RS512KeyPair, RSAKeyPairLike,
9};
10use jwt_simple::reexports::{anyhow, rand};
11use p256::ecdsa::signature::RandomizedDigestSigner;
12
13#[non_exhaustive]
14#[derive(Debug, thiserror::Error)]
15/// Error type for JWT generator operations
16pub enum GeneratorError {
17    #[error("Invalid algorithm {0:?}")]
18    /// The provided algorithm and length provided are not supported by the generator.
19    InvalidAlgorithm(String),
20
21    #[error("Error generating the jwt: {0}.")]
22    /// There was an unexpected error while creating the JWT
23    SigningError(anyhow::Error),
24
25    #[error("Error parsing key: {0}. Key must be provided in pkcs#8 format.")]
26    /// There was an error processing the key.
27    KeyParsing(anyhow::Error),
28
29    #[error("Error serializing values: {0}.")]
30    /// Failed to serialize the claims and headers.
31    Serialization(#[from] serde_json::Error),
32}
33
34trait JwtAlgorithm {
35    fn alg(&self) -> &'static str;
36    fn sign(&self, message: &str) -> Result<Vec<u8>, GeneratorError>;
37
38    fn jwt(&self, claims: JWTClaims) -> Result<String, GeneratorError> {
39        let mut claims = claims;
40        claims.headers.insert("alg".to_string(), self.alg().into());
41        claims.headers.insert("typ".to_string(), "JWT".into());
42        let headers = base64(serde_json::to_vec(&claims.headers)?);
43        let body = base64(serde_json::to_vec(&claims.claims)?);
44
45        let message = format!("{headers}.{body}");
46
47        let signature = base64(self.sign(message.as_str())?);
48
49        Ok(format!("{message}.{signature}"))
50    }
51}
52
53fn base64<D: AsRef<[u8]>>(data: D) -> String {
54    base64::encode_config(data.as_ref(), base64::URL_SAFE_NO_PAD)
55}
56
57macro_rules! jwt_rsa {
58    ($type_name:ty) => {
59        impl JwtAlgorithm for $type_name {
60            fn alg(&self) -> &'static str {
61                Self::jwt_alg_name()
62            }
63
64            fn sign(&self, message: &str) -> Result<Vec<u8>, GeneratorError> {
65                let digest = Self::hash(message.as_bytes());
66                let mut rng = rand::thread_rng();
67                self.key_pair()
68                    .as_ref()
69                    .sign_blinded(&mut rng, self.padding_scheme(), &digest)
70                    .map_err(|e| GeneratorError::SigningError(e.into()))
71            }
72        }
73    };
74}
75
76jwt_rsa!(RS256KeyPair);
77jwt_rsa!(RS384KeyPair);
78jwt_rsa!(RS512KeyPair);
79
80macro_rules! jwt_hmac {
81    ($type_name:ty) => {
82        impl JwtAlgorithm for $type_name {
83            fn alg(&self) -> &'static str {
84                Self::jwt_alg_name()
85            }
86
87            fn sign(&self, message: &str) -> Result<Vec<u8>, GeneratorError> {
88                Ok(self.authentication_tag(message))
89            }
90        }
91    };
92}
93
94jwt_hmac!(HS256Key);
95jwt_hmac!(HS384Key);
96jwt_hmac!(HS512Key);
97
98impl JwtAlgorithm for ES256KeyPair {
99    fn alg(&self) -> &'static str {
100        Self::jwt_alg_name()
101    }
102
103    fn sign(&self, message: &str) -> Result<Vec<u8>, GeneratorError> {
104        let mut digest = hmac_sha256::Hash::new();
105        digest.update(message.as_bytes());
106        let mut rng = rand::thread_rng();
107        let signature: p256::ecdsa::Signature = self
108            .key_pair()
109            .as_ref()
110            .sign_digest_with_rng(&mut rng, digest);
111
112        Ok(signature.to_vec())
113    }
114}
115
116impl JwtAlgorithm for ES384KeyPair {
117    fn alg(&self) -> &'static str {
118        Self::jwt_alg_name()
119    }
120
121    fn sign(&self, message: &str) -> Result<Vec<u8>, GeneratorError> {
122        let mut digest = hmac_sha512::sha384::Hash::new();
123        digest.update(message.as_bytes());
124        let mut rng = rand::thread_rng();
125        let signature: p384::ecdsa::Signature = self
126            .key_pair()
127            .as_ref()
128            .sign_digest_with_rng(&mut rng, digest);
129
130        Ok(signature.to_vec())
131    }
132}
133
134macro_rules! from_pem {
135    ($type_name:ty, $key:expr) => {{
136        Box::new(<$type_name>::from_pem($key).map_err(|e| GeneratorError::KeyParsing(e))?)
137    }};
138}
139
140macro_rules! from_bytes {
141    ($type_name:ty, $key:expr) => {{
142        Box::new(<$type_name>::from_bytes($key.as_bytes()))
143    }};
144}
145
146/// Helper to generate JWT tokens
147pub struct JwtGenerator {
148    alg: Box<dyn JwtAlgorithm>,
149}
150
151impl JwtGenerator {
152    /// Create a new JWT generator. Key must be provided as a PEM with pkcs8 format for RSA and ES algorithms, and plain text for HMAC.
153    pub fn new(
154        algorithm: SigningAlgorithm,
155        length: SigningKeyLength,
156        key: &str,
157    ) -> Result<Self, GeneratorError> {
158        let alg: Box<dyn JwtAlgorithm> = match (&algorithm, &length) {
159            (SigningAlgorithm::Hmac, SigningKeyLength::Len256) => {
160                from_bytes!(HS256Key, key)
161            }
162            (SigningAlgorithm::Hmac, SigningKeyLength::Len384) => {
163                from_bytes!(HS384Key, key)
164            }
165            (SigningAlgorithm::Hmac, SigningKeyLength::Len512) => {
166                from_bytes!(HS512Key, key)
167            }
168            (SigningAlgorithm::Rsa, SigningKeyLength::Len256) => {
169                from_pem!(RS256KeyPair, key)
170            }
171            (SigningAlgorithm::Rsa, SigningKeyLength::Len384) => {
172                from_pem!(RS384KeyPair, key)
173            }
174            (SigningAlgorithm::Rsa, SigningKeyLength::Len512) => {
175                from_pem!(RS512KeyPair, key)
176            }
177            (SigningAlgorithm::Es, SigningKeyLength::Len256) => {
178                from_pem!(ES256KeyPair, key)
179            }
180            (SigningAlgorithm::Es, SigningKeyLength::Len384) => {
181                from_pem!(ES384KeyPair, key)
182            }
183            (SigningAlgorithm::Es, SigningKeyLength::Len512) => {
184                return Err(GeneratorError::InvalidAlgorithm("ES512".to_string()));
185            }
186        };
187
188        Ok(Self { alg })
189    }
190
191    /// Create a JWT token from the provided claims and headers.
192    pub fn jwt(&self, claims: JWTClaims) -> Result<String, GeneratorError> {
193        self.alg.jwt(claims)
194    }
195}
196
197#[cfg(test)]
198mod test {
199    type DynamicSimpleClaims = jwt_simple::claims::JWTClaims<HashMap<String, Value>>;
200    use crate::api::model::{JWTClaims, SigningAlgorithm, SigningKeyLength};
201    use crate::generator::{GeneratorError, JwtGenerator};
202    use chrono::{TimeZone, Utc};
203    use jwt_simple::algorithms::{
204        ECDSAP256PublicKeyLike, ECDSAP384PublicKeyLike, ES256PublicKey, ES384PublicKey, HS256Key,
205        HS384Key, HS512Key, MACLike, RS256PublicKey, RS384PublicKey, RS512PublicKey,
206        RSAPublicKeyLike,
207    };
208    use serde_json::Value;
209    use std::collections::HashMap;
210
211    // openssl ecparam -name secp256r1 -genkey -noout -out jwtES256key.pem
212    // openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in jwtES256key.pem -out jwtES256key-pkcs8.pem
213    // @@Credential-Scanning-Service-Mock@@
214    const TEST_EC_PRIVATE_KEY: &str = "-----BEGIN PRIVATE KEY-----
215MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgyzC5ONgTRTCKUko8
216elZbv5YjMntLt/exJgdjmm1l7IWhRANCAARLDDmGlhxW5yzgu7SrO86MtQcKtm1j
2172ydR/7bsAOVDGeQB6kVBqvOGZXKmVaT8X2mBc/VZrZkYh5PVapnMngmC
218-----END PRIVATE KEY-----";
219
220    // openssl ec -in ./jwtES256key-pkcs8.pem -pubout -out jwtES256key-pkcs8-public.pem
221    // @@Credential-Scanning-Service-Mock@@
222    const TEST_EC_PUBLIC_KEY: &str = "-----BEGIN PUBLIC KEY-----
223MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESww5hpYcVucs4Lu0qzvOjLUHCrZt
224Y9snUf+27ADlQxnkAepFQarzhmVyplWk/F9pgXP1Wa2ZGIeT1WqZzJ4Jgg==
225-----END PUBLIC KEY-----";
226
227    // openssl ecparam -name secp384r1 -genkey -noout -out jwtES384key.pem
228    // openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in jwtES384key.pem -out jwtES384key-pkcs8.pem
229    // @@Credential-Scanning-Service-Mock@@
230    const TEST_EC_PRIVATE_KEY_384: &str = "-----BEGIN PRIVATE KEY-----
231MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDA8W5KSitwokEoKlCP7
232h6DdD7fYD567VjEEEl0L9LcpCfrUWp6CMhTfLoyyj7CoW8qhZANiAARTfi70JZsD
233Vf0iS0Gw3+8ylH/7IQwRA3iZpHC5KQDEOrgXZmcEocgxkZD/Ud0X/tZVwJNAqVt9
234XhvK7gvogAUePeAklVfRJTuZuJhb7x3NbAczz43nHvLtgSHIWH1nyrk=
235-----END PRIVATE KEY-----";
236
237    // openssl ec -in ./jwtES384key-pkcs8.pem -pubout -out jwtES384key-pkcs8-public.pem
238    // @@Credential-Scanning-Service-Mock@@
239    const TEST_EC_PUBLIC_KEY_384: &str = "-----BEGIN PUBLIC KEY-----
240MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEU34u9CWbA1X9IktBsN/vMpR/+yEMEQN4
241maRwuSkAxDq4F2ZnBKHIMZGQ/1HdF/7WVcCTQKlbfV4byu4L6IAFHj3gJJVX0SU7
242mbiYW+8dzWwHM8+N5x7y7YEhyFh9Z8q5
243-----END PUBLIC KEY-----";
244
245    // @@Credential-Scanning-Service-Mock@@
246    const TEST_RSA_PRIVATE_KEY: &str = "-----BEGIN PRIVATE KEY-----
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
296AhW8CZzWMXVPOgExNZJBW65D7p/V
297-----END PRIVATE KEY-----";
298
299    // @@Credential-Scanning-Service-Mock@@
300    const TEST_RSA_PUBLIC_KEY: &str = "-----BEGIN PUBLIC KEY-----
301MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvzqxRxjDW7KrjnopVCN/
302SlIQpJ7gZcX/t5lzg6w6fIj5vf7UPxNUtiWraeYqfXBhb/xQO53SJoFDCB2Z0VZD
303WgpOmJf/kgPtAtSCHAb0eGe666Elx+L0LQQU5z1csRAzma7qafzeQGrYbaoKgpXR
304EYj7usvxkopdFITv5xa5Tme+DQPQXgLUIUZRDZEzC/Tm2sTvw7+Y8yeCJCGSfFyn
3056K3l6twmQRJtoNRGRFNlg9rw2cb+1nT9c/08evjy25ywU8uRO2VDsfWx6GjPqFzw
306s6wNnuXPcYWOjNST/HHJfaqDlIVVATIlyEUkdDnmrAM/3RAvqpcfGE/BaSQkDGnU
307ejbqIAFl5Nki9X8bZnNMymFlyY+w4ord6QZlLF7JjsUBhlfbinlibs+Gld23NLjB
308klnARqFVjlULcPHSzRVlHWWL69OoQEXEiwkYS2z+AgkAYF3uTKilw8WuF8GMpoRX
309Mb2v+F6Xp6xHcUHrk2nA8z6CQJyKrBDhwJezznFsyWx15tD1p0OBlWPuFEU4nXvm
3101uWH4QL/xaIwDhvPpzOPZWlWbrkOD5Cg+bc6CzFYeZiGlL3oiRv/3BV7zBrRgC4F
311Dc5XKLWxvBmF09lNRC+c58ADq8R8VqxIwSK09dwfjsJmidtX6XHfCiM5WrPYOEsw
312DGMTi24MRdH9qfphXIXOrW8CAwEAAQ==
313-----END PUBLIC KEY-----";
314
315    fn base64decode<D: AsRef<[u8]>>(data: D) -> Vec<u8> {
316        base64::decode_config(data, base64::URL_SAFE_NO_PAD).unwrap()
317    }
318
319    type Claims = HashMap<String, Value>;
320
321    fn number(map: &Claims, key: &str) -> u64 {
322        map.get(key).unwrap().as_u64().unwrap()
323    }
324
325    fn get_str<'a>(map: &'a Claims, key: &str) -> &'a str {
326        map.get(key).unwrap().as_str().unwrap()
327    }
328
329    fn decode_jwt(jwt: &str) -> (Claims, Claims) {
330        let header: Claims =
331            serde_json::from_slice(base64decode(jwt.split('.').next().unwrap()).as_slice())
332                .unwrap();
333        let claims: Claims =
334            serde_json::from_slice(base64decode(jwt.split('.').nth(1).unwrap()).as_slice())
335                .unwrap();
336        (header, claims)
337    }
338
339    pub fn smoke(alg: SigningAlgorithm, length: SigningKeyLength, key: &str) -> String {
340        let generator = JwtGenerator::new(alg, length, key).unwrap();
341
342        let mut custom_headers = HashMap::new();
343        custom_headers.insert("custom_header".to_string(), Value::from("custom_header"));
344
345        let mut custom_claims = HashMap::new();
346        custom_claims.insert("custom_claim".to_string(), Value::from("custom_claim"));
347
348        let claims = JWTClaims::new(
349            Some(Utc.timestamp_opt(i32::MAX as i64, 0).single().unwrap()),
350            None,
351            None,
352            custom_claims,
353            custom_headers,
354        )
355        .unwrap();
356
357        let jwt = generator.jwt(claims).unwrap();
358
359        println!("{jwt}");
360
361        jwt
362    }
363
364    fn validate(jwt: String, alg: &str) {
365        let (header, claims) = decode_jwt(&jwt);
366
367        assert_eq!(header.len(), 3);
368        assert_eq!(get_str(&header, "alg"), alg);
369        assert_eq!(get_str(&header, "typ"), "JWT");
370        assert_eq!(get_str(&header, "custom_header"), "custom_header");
371
372        assert_eq!(claims.len(), 2);
373        assert_eq!(number(&claims, "exp"), i32::MAX as u64 * 1000 * 1000 * 1000);
374        assert_eq!(get_str(&claims, "custom_claim"), "custom_claim");
375    }
376
377    #[test]
378    pub fn hmac256() {
379        let key = "some";
380        let jwt = smoke(SigningAlgorithm::Hmac, SigningKeyLength::Len256, key);
381        let _: DynamicSimpleClaims = HS256Key::from_bytes(key.as_bytes())
382            .verify_token(jwt.as_str(), None)
383            .unwrap();
384
385        validate(jwt, "HS256");
386    }
387
388    #[test]
389    pub fn hmac384() {
390        let key = "some";
391        let jwt = smoke(SigningAlgorithm::Hmac, SigningKeyLength::Len384, key);
392        let _: DynamicSimpleClaims = HS384Key::from_bytes(key.as_bytes())
393            .verify_token(jwt.as_str(), None)
394            .unwrap();
395
396        validate(jwt, "HS384");
397    }
398
399    #[test]
400    pub fn hmac512() {
401        let key = "some";
402        let jwt = smoke(SigningAlgorithm::Hmac, SigningKeyLength::Len512, key);
403        let _: DynamicSimpleClaims = HS512Key::from_bytes(key.as_bytes())
404            .verify_token(jwt.as_str(), None)
405            .unwrap();
406
407        validate(jwt, "HS512");
408    }
409
410    #[test]
411    pub fn rsa256() {
412        let jwt = smoke(
413            SigningAlgorithm::Rsa,
414            SigningKeyLength::Len256,
415            TEST_RSA_PRIVATE_KEY,
416        );
417
418        let _: DynamicSimpleClaims = RS256PublicKey::from_pem(TEST_RSA_PUBLIC_KEY)
419            .unwrap()
420            .verify_token(jwt.as_str(), None)
421            .unwrap();
422
423        validate(jwt, "RS256");
424    }
425
426    #[test]
427    pub fn rsa384() {
428        let jwt = smoke(
429            SigningAlgorithm::Rsa,
430            SigningKeyLength::Len384,
431            TEST_RSA_PRIVATE_KEY,
432        );
433
434        let _: DynamicSimpleClaims = RS384PublicKey::from_pem(TEST_RSA_PUBLIC_KEY)
435            .unwrap()
436            .verify_token(jwt.as_str(), None)
437            .unwrap();
438
439        validate(jwt, "RS384");
440    }
441
442    #[test]
443    pub fn rsa512() {
444        let jwt = smoke(
445            SigningAlgorithm::Rsa,
446            SigningKeyLength::Len512,
447            TEST_RSA_PRIVATE_KEY,
448        );
449
450        let _: DynamicSimpleClaims = RS512PublicKey::from_pem(TEST_RSA_PUBLIC_KEY)
451            .unwrap()
452            .verify_token(jwt.as_str(), None)
453            .unwrap();
454
455        validate(jwt, "RS512");
456    }
457
458    #[test]
459    pub fn es256() {
460        let jwt = smoke(
461            SigningAlgorithm::Es,
462            SigningKeyLength::Len256,
463            TEST_EC_PRIVATE_KEY,
464        );
465
466        let _: DynamicSimpleClaims = ES256PublicKey::from_pem(TEST_EC_PUBLIC_KEY)
467            .unwrap()
468            .verify_token(jwt.as_str(), None)
469            .unwrap();
470
471        validate(jwt, "ES256");
472    }
473
474    #[test]
475    pub fn es384() {
476        let jwt = smoke(
477            SigningAlgorithm::Es,
478            SigningKeyLength::Len384,
479            TEST_EC_PRIVATE_KEY_384,
480        );
481
482        let _: DynamicSimpleClaims = ES384PublicKey::from_pem(TEST_EC_PUBLIC_KEY_384)
483            .unwrap()
484            .verify_token(jwt.as_str(), None)
485            .unwrap();
486
487        validate(jwt, "ES384");
488    }
489
490    #[test]
491    pub fn es512() {
492        let result = JwtGenerator::new(
493            SigningAlgorithm::Es,
494            SigningKeyLength::Len512,
495            TEST_EC_PRIVATE_KEY_384,
496        );
497
498        let Err(GeneratorError::InvalidAlgorithm(_)) = result else {
499            panic!("should not create a validator")
500        };
501    }
502
503    #[test]
504    pub fn invalid_key() {
505        let result = JwtGenerator::new(
506            SigningAlgorithm::Es,
507            SigningKeyLength::Len256,
508            TEST_EC_PRIVATE_KEY_384,
509        );
510
511        let Err(GeneratorError::KeyParsing(_)) = result else {
512            panic!("should not create a validator")
513        };
514    }
515}