pdk-jwt-lib 1.10.0

PDK JWT Library
Documentation
// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

//! # Elliptic Curve Signature Validator
//!
//! This module provides signature validation for elliptic curve algorithms
//! (ES256, ES384) used in JWT tokens.

use std::time::{SystemTime, UNIX_EPOCH};

use elliptic_curve::pkcs8::LineEnding::CR;
use elliptic_curve::pkcs8::{AssociatedOid, EncodePublicKey};
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
use elliptic_curve::{CurveArithmetic, JwkParameters, PublicKey};
use jwt_simple::prelude::{
    Duration, ECDSAP256PublicKeyLike, ECDSAP384PublicKeyLike, ES256PublicKey, ES384PublicKey,
    VerificationOptions,
};

use super::signature_validator::SignatureValidation;
use crate::error::jwt_error::JWTError;
use crate::model::claims::{CustomClaims, JWTClaims, TryFromTrustedJWTSimpleClaims};
use crate::model::signing_algorithm::SigningKeyLength;
use crate::model::untrusted_token::untrusted_jwt::UntrustedJWT;
use crate::parser::es_pk_parser::parse_es_pk;

// Signature validator for ES algorithm.
// Supports key lengths 256 and 384.
pub struct EsSignatureValidator<C: CurveArithmetic> {
    key_length: SigningKeyLength,
    key: PublicKey<C>,
}

impl<C> EsSignatureValidator<C>
where
    C: CurveArithmetic + JwkParameters + AssociatedOid,
    <C as CurveArithmetic>::AffinePoint: FromEncodedPoint<C>,
    <C as elliptic_curve::Curve>::FieldBytesSize: ModulusSize,
    <C as CurveArithmetic>::AffinePoint: ToEncodedPoint<C>,
{
    pub fn new(key_length: SigningKeyLength, key: String) -> Result<Self, JWTError> {
        let key = parse_es_pk(key)?;
        Ok(Self { key_length, key })
    }
}

impl<C> SignatureValidation for EsSignatureValidator<C>
where
    C: CurveArithmetic + JwkParameters + AssociatedOid,
    <C as CurveArithmetic>::AffinePoint: FromEncodedPoint<C>,
    <C as elliptic_curve::Curve>::FieldBytesSize: ModulusSize,
    <C as CurveArithmetic>::AffinePoint: ToEncodedPoint<C>,
{
    fn validate(&self, token: String) -> Result<JWTClaims, JWTError> {
        UntrustedJWT::new(token.as_str())
            .and_then(|token| {
                let str_key = self.key.to_public_key_pem(CR)?;
                as_es_algorithm(&self.key_length, str_key.as_str())?
                    .verify_token(token.value().as_str())
            })
            .and_then(|claims| JWTClaims::try_from_trusted_jwt_simple_claims(claims, token))
    }
}

fn full_tolerance() -> core::time::Duration {
    core::time::Duration::from_secs(Duration::from_days(365 * 50).as_secs())
}

enum EsPublicKey {
    ES256 { pk: ES256PublicKey },
    ES384 { pk: ES384PublicKey },
}

impl EsPublicKey {
    fn verify_token(
        &self,
        token: &str,
    ) -> Result<jwt_simple::claims::JWTClaims<CustomClaims>, JWTError> {
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_else(|_| full_tolerance());

        let options = VerificationOptions {
            accept_future: true,
            time_tolerance: Some(Duration::from(now)),
            ..Default::default()
        };

        match self {
            EsPublicKey::ES256 { pk } => pk
                .verify_token::<CustomClaims>(token, Some(options))
                .map_err(JWTError::from),
            EsPublicKey::ES384 { pk } => pk
                .verify_token::<CustomClaims>(token, Some(options))
                .map_err(JWTError::from),
        }
    }
}

fn as_es_algorithm(key_length: &SigningKeyLength, key: &str) -> Result<EsPublicKey, JWTError> {
    match (key_length, key) {
        (SigningKeyLength::Len256, key) => Ok(EsPublicKey::ES256 {
            pk: ES256PublicKey::from_pem(key)?,
        }),
        (SigningKeyLength::Len384, key) => Ok(EsPublicKey::ES384 {
            pk: ES384PublicKey::from_pem(key)?,
        }),
        _ => Err(JWTError::InvalidKeyLength(format!(
            "Invalid configuration: Key length {key_length:?} is not valid for algorithm ES"
        ))),
    }
}

impl From<elliptic_curve::pkcs8::spki::Error> for crate::error::jwt_error::JWTError {
    fn from(e: elliptic_curve::pkcs8::spki::Error) -> Self {
        JWTError::EsPublicKeyParseFailed(format!("ES Public key parsing error: {e}"))
    }
}

#[cfg(test)]
mod tests {
    use super::EsSignatureValidator;
    use crate::model::signing_algorithm::SigningKeyLength;
    use p256::NistP256;
    use p384::NistP384;

    const ES_PUBLIC_KEY_PEM: &str = "-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERqVXn+o+6zEOpWEsGw5CsB+wd8zO
jxu0uASGpiGP+wYfcc1unyMxcStbDzUjRuObY8DalaCJ9/J6UrkQkZBtZw==
-----END PUBLIC KEY-----";

    const ES_384_PUBLIC_KEY_PEM: &str = "-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEjjUFluy6cFrvj/UQwTJoEhPLEJSrKkx0
Rqxc0HEHdFuj0dpj977K9LoYRvZopFk8roS1DW3wghytCEfnW6AWNiesqa4S4iux
oomerb1S3+mXzX4p5459w9axcam9qgjo
-----END PUBLIC KEY-----";

    fn es256_validator() -> EsSignatureValidator<NistP256> {
        EsSignatureValidator::new(SigningKeyLength::Len256, String::from(ES_PUBLIC_KEY_PEM))
            .unwrap()
    }

    fn es384_validator() -> EsSignatureValidator<NistP384> {
        EsSignatureValidator::new(
            SigningKeyLength::Len384,
            String::from(ES_384_PUBLIC_KEY_PEM),
        )
        .unwrap()
    }

    mod es_validator_256 {
        use crate::error::jwt_error::JWTErrorEnumKind;
        use crate::validator::es_validator::tests::es256_validator;
        use crate::validator::es_validator::SignatureValidation;
        use crate::validator::signature_validator::tests::assert_error_kind;
        use test_case::test_case;

        extern crate test_case;

        #[test]
        fn validation_ok_and_jwt_parsed() {
            let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJleHAiOjkyMDA5ODUyNH0.ba5H-gFMcWnu-02y_5ITZVX4FSw2d9zE6BJJCWulWOMMuXaSX4-lBVPCjMpSPBE6fNlnWg4Po6bUf0gRdZCAlg";

            let validation = es256_validator().validate(token.to_string());

            assert!(validation.is_ok());

            let jwt_claims = validation.unwrap();

            // Check token was correctly parsed
            assert_eq!(jwt_claims.get_header("alg").unwrap(), "ES256");
            assert_eq!(
                jwt_claims.expiration().unwrap().to_string(),
                "1999-02-27 06:55:24 UTC"
            );
        }

        #[test]
        fn validation_fails_when_token_not_sent() {
            let token = "";

            let validation = es256_validator().validate(token.to_string());

            assert_error_kind(&validation, JWTErrorEnumKind::ValidationFailed);
        }

        #[test]
        fn unparseable_token_is_rejected() {
            let token = "WhatAmI?AnInvalidToken!";

            let validation = es256_validator().validate(token.to_string());

            assert_error_kind(&validation, JWTErrorEnumKind::ValidationFailed);
        }

        #[test]
        fn validation_fails_on_invalid_signature() {
            let token = "eyJraWQiOiI5OWNiY2EzYS1kZmM4LTQ3NjgtODQ4MC0yZmIwZWVjZTkyZjEiLCJhbGciOiJFUzI1NiJ9.ew0KICAic3ViIjogIjEyMzQ1Njc4OTAiLA0KICAibmFtZSI6ICJBbmlzaCBOYXRoIiwNCiAgImlhdCI6IDE1MTYyMzkwMjINCn0.OS4wEFUvkfHG_wOPFBDVq04F7NDUvvwFmTR147hoP6IjqmwkDBeslsN_5YMcqGR8wztvQYmPi785xJshja9uVA";

            let validation = es256_validator().validate(token.to_string());

            assert_error_kind(&validation, JWTErrorEnumKind::ValidationFailed);
        }

        #[test]
        fn validation_fails_on_missing_signature() {
            let token = "eyJraWQiOiI5NDE2YWY5ZS1jMTZlLTRjM2UtYmNkNy1kODFmMTdkZjBhN2YiLCJhbGciOiJFUzI1NiJ9.ew0KfQ.X_HB6PXUBcMlFjPAZYFRpgJLIzZO6ADcrqnSS4Sq2nVdcQtjkknuwxptjyFpNU5iP7PAFjYwRSMubgh4cQHNqA";

            let validation = es256_validator().validate(token.to_string());

            assert_error_kind(&validation, JWTErrorEnumKind::ValidationFailed);
        }

        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.e30.zWty50iXfn_2RtgdvqJ-A7xhkqSeO2UtAa_W9-RASE7ofEkGArOffQUOtQv76v1tQXDUqlQFf9FOms4FKPaQKA"; "verifyValidToken 256")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJleHAiOjkyMDA5ODUyNH0.ba5H-gFMcWnu-02y_5ITZVX4FSw2d9zE6BJJCWulWOMMuXaSX4-lBVPCjMpSPBE6fNlnWg4Po6bUf0gRdZCAlg"; "exceptionNotThrownOnExpiredTokenWithNoClaimValidation 256")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.e30.zWty50iXfn_2RtgdvqJ-A7xhkqSeO2UtAa_W9-RASE7ofEkGArOffQUOtQv76v1tQXDUqlQFf9FOms4FKPaQKA"; "exceptionNotThrownOnMissingClaim 256")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJleHAiOjMyODAzOTQwOTh9.0rFDoK4mv20SBw2rhNIXcNb4gCIElIzXm9pZYyi0XJ3wvFP9-NmRtOBhBOhEiF4S6tpw2u1Mjvv05yQbqsWfRA"; "verifyNotExpiredToken 256")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJleHAiOjkyMDA5ODUyNH0.ba5H-gFMcWnu-02y_5ITZVX4FSw2d9zE6BJJCWulWOMMuXaSX4-lBVPCjMpSPBE6fNlnWg4Po6bUf0gRdZCAlg"; "exceptionNotThrownOnExpiredToken 256")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJuYmYiOjgyMDA5ODUyNH0.jpshS_OqCYjhHlji9vLFlQ4gRFeHXOwx4TzDGpz3lKzgIqGpYgXvuspVrDAZNsbt_WUq4XkynJj7I1SKXdljUg"; "verifyTokenWithNbf 256")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJuYmYiOjMyODAzOTQwOTh9.BFmaYwAqljy5T7CWwndf_5hwJljasmCNwyM8paOqtBULbyZBrdpT9ZK1co2lD60IdFvAivmEuWEZqpldK3CuJg"; "exceptionNotThrownNotYetActiveToken 256")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJmb28ifQ.1urtAG-oabmrWjH3OVxu--e0UPa5dpxXtZ7CFJGQxFG0IGzPM4LLyxBenWs-9Ue07dPXISuBa77yMTzmuqcLBQ"; "verifyTokenWithAud 256")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOlsiZXRjIiwiZm9vIiwiYmxhaCJdfQ.wEQIdgpzCMK4O3f9obSVit7-IJTOvJm8fWY9j0w-9vJxyxU2zCto7oCx5B2Z8OmWS9oD4bvc1KlEpd_-i7Vi2A"; "verifyTokenWithAudArray 256")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOlsiZXRjIiwiZm9vIGJhciIsImJsYWgiXX0.rtdfw_0B0OVyzRg86IfvXpymK4ukg5vVLxsCKtRj9soxk_4X-xSyDgv9sN_cTfTPOq1upsotUFBoWMhIEUve2Q"; "verifyTokenWithAudiencesWithSpaces 256")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOlsiZXRjIiwiRm9vIiwiYmxhaCJdfQ.Wk6KIMh858nuaa2caoRNiUahZl9KlJbAZ5AgtSUMH-96hJs7naNfzeux-cZ-tuil6ePTLvjwsranIAvDWznamw"; "verifyTokenWithAudiencesCaseSensitive 256")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOlsiZXRjIiwiYmxhaCJdfQ.SFdcVoIGlJTDAkao6QfjOQEDPq4lAzVjcWQOBXUZ3_EqIWUCJ6XEohhghuux8QbSDmhj36mVimrBbQ4IcyvYJw"; "exceptionNotThrownWhenInvalidAudToken 256")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJmb28iLCJuYmYiOjgyMDA5ODUyNCwibmFtZSI6IkpvaG4gRG9lIiwic2NvcGVzIjpbInJlYWQiLCJ3cml0ZSJdLCJleHAiOjMyODAzOTQwOTh9.xfaGYUA1xGgYQjdiWFT8JT8AHdZochXx8VkvP1dnUgV-SmdefElAy3JaXKfpnVFM-7DvcnEeUYycQCNL3qS9lA"; "checkAllValidations 256")]
        fn verify_valid_token_256(token: &str) {
            let validation = es256_validator().validate(token.to_string());

            assert!(validation.is_ok());
        }
    }

    mod es_validator_384 {
        use crate::error::jwt_error::JWTErrorEnumKind;
        use crate::validator::es_validator::tests::es384_validator;
        use crate::validator::es_validator::SignatureValidation;
        use crate::validator::signature_validator::tests::assert_error_kind;
        use test_case::test_case;

        extern crate test_case;

        #[test]
        fn validation_ok() {
            let token = "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNjU4NDMwNjg2LCJleHAiOjE2NTg5NDg2MzF9.Bm6E2fuWizA8WObF6tWl80Gyak79H6FaD4Fxn9d0Gr-IGih5PvntcXHHl18p1DYFLsrt9xPfIAAaWsIk6VfrtCNb65Y2nzqDJUCNcYtFBvxY8NPY2dRLC3Gl5HAqVvex";

            let validation = es384_validator().validate(token.to_string());

            assert!(validation.is_ok());
        }

        #[test]
        fn validation_fails_when_token_not_sent() {
            let token = "eyJraWQiOiI5NDE2YWY5ZS1jMTZlLTRjM2UtYmNkNy1kODFmMTdkZjBhN2YiLCJhbGciOiJFUzI1NiJ9.ew0KfQ.X_HB6PXUBcMlFjPAZYFRpgJLIzZO6ADcrqnSS4Sq2nVdcQtjkknuwxptjyFpNU5iP7PAFjYwRSMubgh4cQHNqA";

            let validation = es384_validator().validate(token.to_string());

            assert_error_kind(&validation, JWTErrorEnumKind::ValidationFailed);
        }

        #[test]
        fn unparseable_token_is_rejected() {
            let token = "WhatAmI?AnInvalidToken!";

            let validation = es384_validator().validate(token.to_string());

            assert_error_kind(&validation, JWTErrorEnumKind::ValidationFailed);
        }

        #[test]
        fn validation_fails_on_invalid_signature() {
            let token = "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFuaXNoIE5hdGgiLCJpYXQiOjE1MTYyMzkwMjJ9.6adcW-2ww22MC3n41o5b6wULVolXno4md0glQR1U9hfIpbyyWQS2U-947x4mnc6bx-pxKO9SjnLtv7rsvOjvP300K5WayPIISQ0lc7CDkKH2s3oT1Bw5gHVtKfYT5Gjv";

            let validation = es384_validator().validate(token.to_string());

            assert_error_kind(&validation, JWTErrorEnumKind::ValidationFailed);
        }

        #[test]
        fn validation_fails_on_missing_signature() {
            let token = "";

            let validation = es384_validator().validate(token.to_string());

            assert_error_kind(&validation, JWTErrorEnumKind::ValidationFailed);
        }

        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.e30.LLv51B6Y7ksfwFNLeMuF_nWNVHjvZwU75MC6uVJCFqYag7RogTHuEMIc5PsE42fwAlsKaY8pWQbzLeMkl7-vC0fkMvj7YDIowWYE15gfHigzbrelwpgFZ8k6SzdIKZJ2"; "verifyValidToken 384")]
        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJleHAiOjkyMDA5ODUyNH0._IIq-sOy4JExOTJphYrxcogGc7dEc80xNLqz0FmY_6FAgH_W1E6ihitVnMwAg1WUKJZAPIuLCZtdCiWi69aH5Uz_YGKPZjppOqETgHuoQ8C89z2gQ313LgoyBv-ebvpB"; "exceptionNotThrownOnExpiredTokenWithNoClaimValidation 384")]
        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.e30.6BJi3OGsiXOyRwUfu663r9SL0NzuYkdOQ_MS46WfGSWBXHc8WS_VpKlt4wZXaZMOQKndDCPdKbvXSLnrJ0pNWGiwQ7IufviWxhXkMJL6jll9Jwf1-y8EXQt1NiY8aYvR"; "exceptionNotThrownOnMissingClaim 384")]
        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJleHAiOjMyODAzOTQwOTh9.LxipZW5nkk-T-2mRxLzmEE44lKy4cc3eHLWI3LfDPHFO_qhZIqaGxhS7SwBk6HNCiJ61EOHq-YjehMa1xxaKupfa6n7MlVm9EqHhPXyONqfiH3BchxhqC1KbDv9TrKyz"; "verifyNotExpiredToken 384")]
        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJleHAiOjkyMDA5ODUyNC4zMjF9.Xyn-wcsh_SCoBFJZkQUC5jeJYSuXpF1k7UbgQN0jwZJSBCCkxOYjzevuCXtZJsnySmYkyuP-7K6YPtLpiS7Nmqz--1AN6S6u4l3TO-20PjybFhEckZEGVYf98esmcwHm"; "exceptionNotThrownOnExpiredToken 384")]
        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJuYmYiOjgyMDA5ODUyNC40MzN9.Db2CqHjYHIvB_pb2_p3GfI57KRHGa_MYJ3dF5Or4vM4JQG5E756Lm21_dSqYDJnA6roan7z92uuoqpFRRNmdHjTz8zgP_QBcdSQhUsogA3YlE27o-oveGsjDOWQG_88l"; "verifyTokenWithNbf 384")]
        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJuYmYiOjMyODAzOTQwOTguMzQyfQ.H3DWycc7O1QY4_V8alWXQf9cuxYmRPkKdmaiQ8yTVj5LaOTDxoVeFYusQWC8NQ0ZbYrEqZNHLcZlTQZ55fsaYPXrRgLaeqCQ_BGeMJZqj8btzoZ8AKGUtWYbUhCD6UfL"; "exceptionNotThrownNotYetActiveToken 384")]
        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJmb28ifQ.pdejdj6fkVgxQLzW_hj40tbk2Nc2WFcfrzdtIT2ZKbd9UA24nO_eYjz411eR8wN0uYMPlgkGXeMFRCw-TMmmTvhhqLy-3tgFfdMtfD6HkxVhFRSK1q7ZUWOfumNmspgR"; "verifyTokenWithAud 384")]
        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZXRjIiwiZm9vIiwiYmxhaCJdfQ.KhcqpcQdiOPy_fiVjDqmETW1uxgKoVlQqF4aMvterke0i6GdD0d5Bst5_wxz4l4Q54Ybw6uLHRwXT7Afc9RTDPBQqRLf2CgidRu72NukcXufYpJyPsyelneETSAC855N"; "verifyTokenWithAudArray 384")]
        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZXRjIiwiZm9vIGJhciIsImJsYWgiXX0.v-yysmA64_SJ0w9eZPL3NNQFCpJYweFvy7LJP84N_6NDA1RjAicAi7Gt6DIAdG-cFG0Jn8NJM4DdwRBJUycGrOILCOgmio-bh63S-FwBLk3ExAJzrAr_yiMWLEvekgR6"; "verifyTokenWithAudiencesWithSpaces 384")]
        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZXRjIiwiRm9vIiwiYmxhaCJdfQ.irwNz94eGFFXblZSxC4OGAs3pzR07b1VAaEGfzu_2QcTAHqMSLw1c7HmeBDczwNY89X1smiVhFx6R1COJ8CA9c1h9vrYe1ED_LfElVfvTSpYHNcPWgBLFsvQI7k98xL4"; "verifyTokenWithAudiencesCaseSensitive 384")]
        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZXRjIiwiYmxhaCJdfQ.9C3CuglaBOiKoJQycEjVg2NOap2RbQVhSdqJk6V3tC3mK3OzxFouPuzb7ZLNlJh73UbD-gHXZxieFTNAc60dcYVt-tuUMYA9Atp-FZTeoZ973a-KI_DHb9BLiLo0hgYi"; "exceptionNotThrownWhenInvalidAudToken 384")]
        #[test_case("eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJmb28iLCJuYmYiOjgyMDA5ODUyNCwibmFtZSI6IkpvaG4gRG9lIiwic2NvcGVzIjpbInJlYWQiLCJ3cml0ZSJdLCJleHAiOjMyODAzOTQwOTh9.4_AS9rscqomVqrAuF3JuV9WT-dahxNik3U5ou-nLmYK8Is-7ddkP1tLfQm5ufcRUXrN9HbyXdNy9Afr8TP0Vd72v45HKEZancXaghzlhmGGROesshVwBRltC1XLYEsIE"; "checkAllValidations 384")]
        fn verify_valid_token_384(token: &str) {
            let validation = es384_validator().validate(token.to_string());

            assert!(validation.is_ok());
        }
    }
}