Skip to main content

pdk_jwt_lib/validator/
signature_validator.rs

1// Copyright (c) 2026, Salesforce, Inc.,
2// All rights reserved.
3// For full license text, see the LICENSE.txt file
4
5//! JWT signature validation
6//!
7//! This module provides JWT signature validation functionality supporting multiple
8//! algorithms including HMAC, RSA, and ES. It validates JWT tokens and
9//! extracts claims when the signature is valid.
10//!
11//! ## Primary types
12//!
13//! - [`SignatureValidation`]: trait for signature validation implementations
14//! - [`SignatureValidator`]: main validator that creates algorithm-specific validators
15//!
16
17use crate::{
18    error::jwt_error::JWTError,
19    model::{
20        claims::JWTClaims,
21        signing_algorithm::{SigningAlgorithm, SigningKeyLength},
22    },
23};
24use std::{cell::RefCell, rc::Rc};
25
26/// Trait that all signature algorithm validators should implement.
27pub trait SignatureValidation {
28    /// Validates whether a JWT token (passed in [`String`] format) is valid.
29    /// If the token is valid, the result is the content of the JWT extracted into a [`JWTClaims`].
30    /// When not valid, it results in a [`JWTError`].
31    fn validate(&self, token: String) -> Result<JWTClaims, JWTError>;
32}
33
34/// Main signature validator implementation. It receives the specifications of the signature check:
35///   * Algorithm
36///   * Key Length
37///   * Verifying Key (RSA, ES) or Signing Secret (HMAC)
38///
39/// And it generates the corresponding specific validator for the use case.
40pub struct SignatureValidator {
41    validator: Rc<RefCell<dyn SignatureValidation>>,
42}
43
44impl SignatureValidator {
45    /// Create a new [`SignatureValidator`] from the given algorithm, key length and key.
46    pub fn new(
47        algorithm: SigningAlgorithm,
48        key_length: SigningKeyLength,
49        key: String,
50    ) -> Result<Self, JWTError> {
51        #[cfg(fips)]
52        {
53            Ok(SignatureValidator {
54                validator: Rc::new(RefCell::new(
55                    super::proxy_wasm_validator::ProxyWasmSignatureValidator::new(
56                        algorithm,
57                        key_length,
58                        super::proxy_wasm_validator::Key::Pem(key),
59                    )?,
60                )),
61            })
62        }
63
64        #[cfg(not(fips))]
65        match algorithm {
66            SigningAlgorithm::Rsa => Ok(SignatureValidator {
67                validator: Rc::new(RefCell::new(
68                    super::rsa_validator::RsaSignatureValidator::new(key_length, key)?,
69                )),
70            }),
71            SigningAlgorithm::Es => match key_length {
72                SigningKeyLength::Len256 => Ok(SignatureValidator {
73                    validator: Rc::new(RefCell::new(super::es_validator::EsSignatureValidator::<
74                        p256::NistP256,
75                    >::new(key_length, key)?)),
76                }),
77                SigningKeyLength::Len384 => Ok(SignatureValidator {
78                    validator: Rc::new(RefCell::new(super::es_validator::EsSignatureValidator::<
79                        p384::NistP384,
80                    >::new(key_length, key)?)),
81                }),
82                _ => Err(JWTError::InvalidKeyLength(
83                    "Signing key length is invalid for ES algorithm".to_string(),
84                )),
85            },
86            SigningAlgorithm::Hmac => Ok(SignatureValidator {
87                validator: Rc::new(RefCell::new(
88                    super::hmac_validator::HmacSignatureValidator::new(key_length, key),
89                )),
90            }),
91        }
92    }
93
94    /// Validate a JWT token.
95    pub fn validate(&self, token: String) -> Result<JWTClaims, JWTError> {
96        self.validator.borrow().validate(token)
97    }
98}
99
100#[cfg(test)]
101pub mod tests {
102    use crate::error::jwt_error::{JWTError, JWTErrorEnumKind};
103
104    // Helper to perform assertions over the error handling in different SignatureValidation implementators
105    pub fn assert_error_kind<T>(validation: &Result<T, JWTError>, kind: JWTErrorEnumKind)
106    where
107        T: std::fmt::Debug,
108    {
109        assert!(validation.is_err());
110        assert_eq!(
111            JWTErrorEnumKind::from(validation.as_ref().unwrap_err()),
112            kind
113        );
114    }
115
116    #[cfg(not(fips))]
117    mod signature_validator_test {
118        use crate::model::signing_algorithm::{SigningAlgorithm, SigningKeyLength};
119        use crate::validator::signature_validator::SignatureValidator;
120
121        const RSA_PUBLIC_KEY_PEM_256: &str = "-----BEGIN PUBLIC KEY-----
122MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnFRZJoSbY3lJmruwlXQM
123wiOlr4EtBJHSj7IrCYqx9rFEW+JUwoe5iMZl33D6tUsr1GsG50R7JL2KvxHrj0u6
124A3A9beI41XdRfJatugaffroLuZCUYla097uVSQacUSBfBVEsT0TU9MrxBW0HGIXz
125dGwuA9GRncgk7kcfPgJLjwzcBBZATVaASO28Rrf6eQ12w0UNJL4XM+/yPyQatbvO
126D5KqX8ue2a0uO+7yE8x0A99UMIHYo1zedp+Qr+p0+/5VTWjEZT5I6JGUIMjSVqn0
127L0z0WMKJtSdG/CQpZyGnWBz6irOd5xJHAyH7mr02dOJVSXMJXvchCEMMlWghqlqQ
128XwIDAQAB
129-----END PUBLIC KEY-----";
130
131        #[test]
132        fn validation_success() {
133            let token: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.e30.AC3VWMTRNDUh2S2MbT3SSzVg4P7BykOCWYSlMRYgh7xUGo4lisfjaTKpqydtoouB-mJaWRtq09rnP1BG1IMLXxPjYNPsVbTvcpktifS_qAnzjLtNpki_a7H1Uxey7mPn6MQf0bzD4inYb_zz7OgPb6U57l8B7kHKhA7vS6-a56zOxDnrZm2pwtNhkoXWBfQ6LiRWjfPOQUED3ep3YZ-BQR5sT3KUNV_nrgi6Qhc1wPOoTbEaLbjCQjnML6jxpJxpXKL2zL8ARHy-avxqtwCiWW3LQ-DQhneEVYzZ79q-8FKD8GcII9g3MCX5MZVmpYS3Z3WPP1Xzb4IVtUUYK6dogg";
134            let signature_validator = SignatureValidator::new(
135                SigningAlgorithm::Rsa,
136                SigningKeyLength::Len256,
137                RSA_PUBLIC_KEY_PEM_256.to_string(),
138            )
139            .unwrap();
140
141            let validation = signature_validator.validate(token.to_string());
142
143            assert!(validation.is_ok());
144        }
145    }
146}