diff --git a/.changes/typed-jwt-verifier.md b/.changes/typed-jwt-verifier.md new file mode 100644 index 00000000..8a8d81af --- /dev/null +++ b/.changes/typed-jwt-verifier.md @@ -0,0 +1,8 @@ +--- +"rscrypto" = "minor" +--- + +RSA JWT/JWS verification is now bound to one verifier-owned +`RsaJwtAlgorithm`. Peer-controlled `alg` metadata can only match that fixed +policy; string-to-profile helpers and runtime algorithm-name signing and +verification APIs have been removed. diff --git a/docs/types.md b/docs/types.md index ce2d3541..2392b9d0 100644 --- a/docs/types.md +++ b/docs/types.md @@ -34,6 +34,8 @@ Prelude: `rscrypto::prelude` re-exports `Aead`, `Checksum`, other randomized APIs expose caller-supplied entropy where deterministic or constrained use needs it. - Bind RSA generic signing and verification through `RsaPrivateKey::signer(profile)` and `RsaPublicKey::verifier(profile)` so the padding and hash policy are explicit. +- Bind JWT/JWS verification through `RsaPublicKey::jwt_verifier(algorithm)`. The verifier owns one typed + `RsaJwtAlgorithm`; peer-controlled `alg` metadata can match that policy but cannot select it. ## Checksums @@ -139,6 +141,7 @@ Features: `signatures` / `key-exchange` or `ecdsa` / `ed25519` / `rsa` / `x25519 | `Ed25519Keypair` | -- | RFC 8032 | | `RsaPublicKey`, `RsaPrivateKey`, `RsaPrivateKeyParts`, `RsaX509PublicKey`, `RsaPublicScratch`, `RsaPrivateScratch` | variable | RFC 8017 / RFC 4055 | | `RsaSignatureSigner`, `RsaSignatureVerifier` | profile-bound wrappers | RFC 8017 / RFC 4055 | +| `RsaJwtAlgorithm`, `RsaJwtVerifier` | verifier-owned JWT/JWS policy | RFC 7515 / RFC 8725 | | `RsaSignatureProfile`, `RsaPssProfile`, `RsaPkcs1v15Profile`, `RsaOaepProfile`, `RsaPublicKeyPolicy`, `RsaKeyGenerationContract` | -- | RFC 8017 / RFC 4055 / FIPS 186-5 / protocol-specific profiles | | `RsaPublicExponent`, `RsaPublicExponentPolicy`, `RsaTlsSignatureSchemes`, `RsaX509PublicKeyAlgorithm` | -- | RSA policy / protocol mapping | | `X25519SecretKey` / `X25519PublicKey` / `X25519SharedSecret` | 32B each | RFC 7748 | @@ -214,7 +217,7 @@ also has `seal_random_to_vec`. | `RsaPrivateOpError` | RSA private operation, padding, entropy, or fault-check failure | Reject input; do not expose reason to peer | | `RsaEncryptionError` | RSA public encryption shape, padding, or entropy failure | Fix input / entropy source | | `RsaKeyGenerationError` | RSA key generation policy or entropy failure | Adjust key size/policy or entropy source | -| `RsaProtocolAlgorithmError` | Unsupported/confused JWT/COSE/TLS/X.509 RSA selector | Reject algorithm mapping | +| `RsaProtocolAlgorithmError` | Unsupported/confused COSE/TLS/X.509 RSA selector | Reject algorithm mapping | | `AsconCxofCustomizationError` | Customization > 256 bytes | Shorten string | | `InvalidHexError` | Hex decode failure | Fix input | | `platform::OverrideError` | Override after detection init | Set before first call | diff --git a/fuzz/target_impls/auth_rsa_protocol.rs b/fuzz/target_impls/auth_rsa_protocol.rs index ddcf0122..4c51e398 100644 --- a/fuzz/target_impls/auth_rsa_protocol.rs +++ b/fuzz/target_impls/auth_rsa_protocol.rs @@ -1,4 +1,4 @@ -use rscrypto::{RsaPublicKey, RsaSignatureProfile, RsaX509PublicKey}; +use rscrypto::{RsaJwtAlgorithm, RsaPublicKey, RsaSignatureProfile, RsaX509PublicKey}; use rscrypto_fuzz::{FuzzInput, some_or_return, split_at_ratio}; const RSA3072_SPKI: &[u8] = include_bytes!("../../benches/rsa_fixtures/rsa3072_spki.der"); @@ -99,7 +99,8 @@ pub fn run(data: &[u8]) { match mode % 14 { 0 => { key - .verify_jwt_alg_with_scratch("PS256", MESSAGE_PSS, RSA3072_PSS_SHA256, &mut scratch) + .jwt_verifier(RsaJwtAlgorithm::Ps256) + .verify_with_scratch("PS256", MESSAGE_PSS, RSA3072_PSS_SHA256, &mut scratch) .expect("valid JWT PS256 fixture must verify"); } 1 => { @@ -133,7 +134,9 @@ pub fn run(data: &[u8]) { .expect("valid TLS certificate rsa_pkcs1_sha256 fixture must verify"); } 5 => { - let _ = key.verify_jwt_alg_with_scratch(select(&JWT_ALGS, selector), message, signature, &mut scratch); + let _ = key + .jwt_verifier(RsaJwtAlgorithm::Ps256) + .verify_with_scratch(select(&JWT_ALGS, selector), message, signature, &mut scratch); } 6 => { let _ = key.verify_cose_algorithm_id_with_scratch( @@ -175,7 +178,6 @@ pub fn run(data: &[u8]) { let _ = RsaSignatureProfile::from_x509_signature_algorithm_der(message); } 12 => { - let _ = RsaSignatureProfile::from_jwt_alg(select(&JWT_ALGS, selector)); let _ = RsaSignatureProfile::from_cose_algorithm_id(*select(&COSE_ALGORITHMS, selector)); let _ = RsaSignatureProfile::from_tls13_signature_scheme(u16::from_be_bytes([selector, split])); let _ = RsaSignatureProfile::from_tls_certificate_signature_scheme(u16::from_be_bytes([selector, split])); diff --git a/src/auth/mod.rs b/src/auth/mod.rs index 12c34038..8bc53dbc 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -277,11 +277,11 @@ pub use pbkdf2::{diag_pbkdf2_sha256_verify_portable, diag_pbkdf2_sha512_verify_p pub use poly1305::{Poly1305, Poly1305OneTimeKey, Poly1305Tag}; #[cfg(feature = "rsa")] pub use rsa::{ - RsaEncryptionError, RsaKeyError, RsaKeyGenerationContract, RsaKeyGenerationError, RsaOaepProfile, RsaPkcs1v15Profile, - RsaPrivateKey, RsaPrivateKeyParts, RsaPrivateOpError, RsaPrivateScratch, RsaProtocolAlgorithmError, RsaPssProfile, - RsaPublicExponent, RsaPublicExponentPolicy, RsaPublicKey, RsaPublicKeyPolicy, RsaPublicOpError, RsaPublicScratch, - RsaSignatureProfile, RsaSignatureSigner, RsaSignatureVerifier, RsaTlsSignatureSchemes, RsaX509PublicKey, - RsaX509PublicKeyAlgorithm, + RsaEncryptionError, RsaJwtAlgorithm, RsaJwtVerifier, RsaKeyError, RsaKeyGenerationContract, RsaKeyGenerationError, + RsaOaepProfile, RsaPkcs1v15Profile, RsaPrivateKey, RsaPrivateKeyParts, RsaPrivateOpError, RsaPrivateScratch, + RsaProtocolAlgorithmError, RsaPssProfile, RsaPublicExponent, RsaPublicExponentPolicy, RsaPublicKey, + RsaPublicKeyPolicy, RsaPublicOpError, RsaPublicScratch, RsaSignatureProfile, RsaSignatureSigner, + RsaSignatureVerifier, RsaTlsSignatureSchemes, RsaX509PublicKey, RsaX509PublicKeyAlgorithm, }; #[cfg(all(feature = "rsa", feature = "diag"))] pub use rsa::{ diff --git a/src/auth/rsa.rs b/src/auth/rsa.rs index a558c8f9..7c7ecb2c 100644 --- a/src/auth/rsa.rs +++ b/src/auth/rsa.rs @@ -425,13 +425,81 @@ impl fmt::Display for RsaProtocolAlgorithmError { impl core::error::Error for RsaProtocolAlgorithmError {} +/// RSA algorithms supported by the JWT/JWS policy boundary. +/// +/// This type is intentionally not constructible from a string. Applications +/// choose one variant from trusted verifier configuration, then pass the +/// peer-controlled JOSE `alg` value only to [`RsaJwtVerifier::verify`] for an +/// exact match. A token therefore cannot select or widen the verifier's +/// cryptographic policy. +/// +/// Runtime strings cannot construct policy: +/// +/// ```compile_fail +/// use rscrypto::RsaJwtAlgorithm; +/// +/// let algorithm: RsaJwtAlgorithm = "PS256".parse().unwrap(); +/// ``` +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum RsaJwtAlgorithm { + /// RSASSA-PKCS1-v1_5 with SHA-256 (`RS256`). + Rs256, + /// RSASSA-PKCS1-v1_5 with SHA-384 (`RS384`). + Rs384, + /// RSASSA-PKCS1-v1_5 with SHA-512 (`RS512`). + Rs512, + /// RSASSA-PSS with SHA-256 and a 32-byte salt (`PS256`). + Ps256, + /// RSASSA-PSS with SHA-384 and a 48-byte salt (`PS384`). + Ps384, + /// RSASSA-PSS with SHA-512 and a 64-byte salt (`PS512`). + Ps512, +} + +impl RsaJwtAlgorithm { + /// Return the canonical case-sensitive JOSE `alg` value. + #[inline] + #[must_use] + pub const fn as_str(self) -> &'static str { + match self { + Self::Rs256 => "RS256", + Self::Rs384 => "RS384", + Self::Rs512 => "RS512", + Self::Ps256 => "PS256", + Self::Ps384 => "PS384", + Self::Ps512 => "PS512", + } + } + + /// Return the exact RSA signature profile fixed by this algorithm. + #[inline] + #[must_use] + pub const fn signature_profile(self) -> RsaSignatureProfile { + match self { + Self::Rs256 => RsaSignatureProfile::pkcs1v15(RsaPkcs1v15Profile::Sha256), + Self::Rs384 => RsaSignatureProfile::pkcs1v15(RsaPkcs1v15Profile::Sha384), + Self::Rs512 => RsaSignatureProfile::pkcs1v15(RsaPkcs1v15Profile::Sha512), + Self::Ps256 => RsaSignatureProfile::pss(RsaPssProfile::Sha256), + Self::Ps384 => RsaSignatureProfile::pss(RsaPssProfile::Sha384), + Self::Ps512 => RsaSignatureProfile::pss(RsaPssProfile::Sha512), + } + } +} + +impl fmt::Display for RsaJwtAlgorithm { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + /// Typed RSA signature profile. /// /// This is the primitive-layer selector adapters should map to after they have -/// parsed protocol-specific identifiers such as TLS `SignatureScheme`, JWT -/// `alg`, COSE algorithm IDs, or X.509 algorithm parameters. Unsupported -/// protocol algorithms, SHA-1 profiles, and ambiguous parameter encodings -/// should fail before constructing this type. +/// parsed protocol-specific identifiers such as TLS `SignatureScheme`, COSE +/// algorithm IDs, or X.509 algorithm parameters. JWT/JWS uses the narrower +/// [`RsaJwtAlgorithm`] and [`RsaJwtVerifier`] boundary so a token cannot select +/// this profile. Unsupported protocol algorithms, SHA-1 profiles, and ambiguous +/// parameter encodings should fail before constructing this type. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum RsaSignatureProfile { /// RSASSA-PSS with a typed hash/MGF1 profile and explicit salt length. @@ -531,33 +599,6 @@ impl RsaSignatureProfile { } } - /// Map a JWT `alg` value to an RSA signature profile. - /// - /// Only the explicit RSA SHA-2 algorithms from JOSE are accepted. `none`, - /// HMAC, ECDSA, EdDSA, SHA-1, and unknown values fail closed. - /// - /// This is a parser for locally configured or already trusted policy values. - /// When `alg` comes from a JOSE header, use - /// [`RsaPublicKey::verify_expected_jwt_alg`] so the header is checked against - /// a locally pinned profile before verification. - /// - /// # Errors - /// - /// Returns [`RsaProtocolAlgorithmError::UnsupportedAlgorithm`] for any value - /// other than `RS256`, `RS384`, `RS512`, `PS256`, `PS384`, or `PS512`. - #[inline] - pub fn from_jwt_alg(alg: &str) -> Result { - match alg { - "RS256" => Ok(Self::pkcs1v15(RsaPkcs1v15Profile::Sha256)), - "RS384" => Ok(Self::pkcs1v15(RsaPkcs1v15Profile::Sha384)), - "RS512" => Ok(Self::pkcs1v15(RsaPkcs1v15Profile::Sha512)), - "PS256" => Ok(Self::pss(RsaPssProfile::Sha256)), - "PS384" => Ok(Self::pss(RsaPssProfile::Sha384)), - "PS512" => Ok(Self::pss(RsaPssProfile::Sha512)), - _ => Err(RsaProtocolAlgorithmError::UnsupportedAlgorithm), - } - } - /// Map a COSE algorithm ID to an RSA signature profile. /// /// Accepts the explicit COSE RSASSA-PKCS1-v1_5 SHA-2 IDs `-257`, `-258`, @@ -586,40 +627,6 @@ impl RsaSignatureProfile { } } - /// Return `true` if this profile is exactly the profile named by a JOSE - /// `alg` value. - #[inline] - #[must_use] - pub fn matches_jwt_alg(self, alg: &str) -> bool { - match (self, alg) { - (Self::Pkcs1v15(RsaPkcs1v15Profile::Sha256), "RS256") - | (Self::Pkcs1v15(RsaPkcs1v15Profile::Sha384), "RS384") - | (Self::Pkcs1v15(RsaPkcs1v15Profile::Sha512), "RS512") => true, - ( - Self::Pss { - profile: RsaPssProfile::Sha256, - salt_len, - }, - "PS256", - ) => salt_len == RsaPssProfile::Sha256.digest_len(), - ( - Self::Pss { - profile: RsaPssProfile::Sha384, - salt_len, - }, - "PS384", - ) => salt_len == RsaPssProfile::Sha384.digest_len(), - ( - Self::Pss { - profile: RsaPssProfile::Sha512, - salt_len, - }, - "PS512", - ) => salt_len == RsaPssProfile::Sha512.digest_len(), - _ => false, - } - } - /// Return `true` if this profile is exactly the profile named by a COSE /// algorithm ID. #[inline] @@ -1223,6 +1230,18 @@ pub struct RsaSignatureVerifier<'a> { profile: RsaSignatureProfile, } +/// RSA JWT/JWS verifier bound to one key and one locally selected algorithm. +/// +/// A JOSE provider must parse the protected header, reject duplicate members +/// and malformed serialization, and pass the single decoded `alg` string to +/// [`Self::verify`]. This verifier checks that string against its fixed policy; +/// it never maps peer metadata to a signature profile. +#[derive(Clone, Copy)] +pub struct RsaJwtVerifier<'a> { + key: &'a RsaPublicKey, + algorithm: RsaJwtAlgorithm, +} + /// Borrowed RSA private-key CRT components. /// /// These fields contain private key material. Keep values canonical unsigned @@ -1433,6 +1452,16 @@ impl RsaPrivateKey { RsaSignatureSigner::new(self, profile) } + /// Return a signer bound to one typed JWT/JWS algorithm. + /// + /// Use [`RsaJwtAlgorithm::as_str`] when emitting the matching protected + /// header. No runtime algorithm name is accepted by this API. + #[inline] + #[must_use] + pub const fn jwt_signer(&self, algorithm: RsaJwtAlgorithm) -> RsaSignatureSigner<'_> { + self.signer(algorithm.signature_profile()) + } + /// Allocate reusable scratch space for deterministic private operations. /// /// Use this with the `*_with_blinding_factor_and_scratch` methods when a @@ -1660,52 +1689,6 @@ impl RsaPrivateKey { clear_output_on_error(result, out) } - /// Sign a JWT/JWS signing input using an already-parsed JOSE `alg`. - /// - /// Primitive helper only: this is not a JWT, JWS, JOSE, or JSON provider - /// integration. - /// - /// # Errors - /// - /// Returns [`RsaPrivateOpError::UnsupportedAlgorithm`] if `alg` is not an - /// accepted RSA SHA-2 JOSE algorithm. Other errors match - /// [`Self::sign_signature`]. - #[cfg(feature = "getrandom")] - #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))] - #[must_use = "RSA signing failure must be checked; a dropped Result silently discards a failed signature"] - pub fn sign_jwt_alg(&self, alg: &str, message: &[u8], out: &mut [u8]) -> Result<(), RsaPrivateOpError> { - let result = RsaSignatureProfile::from_jwt_alg(alg) - .map_err(|_| RsaPrivateOpError::UnsupportedAlgorithm) - .and_then(|profile| self.sign_signature(profile, message, out)); - clear_output_on_error(result, out) - } - - /// Sign a JWT/JWS signing input using an already-parsed JOSE `alg` and caller-owned scratch. - /// - /// Primitive helper only: this is not a JWT, JWS, JOSE, or JSON provider - /// integration. - /// - /// # Errors - /// - /// Returns [`RsaPrivateOpError::UnsupportedAlgorithm`] if `alg` is not an - /// accepted RSA SHA-2 JOSE algorithm. Other errors match - /// [`Self::sign_signature_with_scratch`]. - #[cfg(feature = "getrandom")] - #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))] - #[must_use = "RSA signing failure must be checked; a dropped Result silently discards a failed signature"] - pub fn sign_jwt_alg_with_scratch( - &self, - alg: &str, - message: &[u8], - out: &mut [u8], - scratch: &mut RsaPrivateScratch, - ) -> Result<(), RsaPrivateOpError> { - let result = RsaSignatureProfile::from_jwt_alg(alg) - .map_err(|_| RsaPrivateOpError::UnsupportedAlgorithm) - .and_then(|profile| self.sign_signature_with_scratch(profile, message, out, scratch)); - clear_output_on_error(result, out) - } - /// Sign a COSE Sig_structure using an already-parsed COSE algorithm ID. /// /// Primitive helper only: this is not a COSE, CBOR, or CWT provider @@ -2438,6 +2421,84 @@ impl crate::traits::Verifier<[u8]> for RsaSignatureVerifier<'_> { } } +impl<'a> RsaJwtVerifier<'a> { + /// Construct a verifier bound to `key` and one locally selected algorithm. + #[inline] + #[must_use] + pub const fn new(key: &'a RsaPublicKey, algorithm: RsaJwtAlgorithm) -> Self { + Self { key, algorithm } + } + + /// Return the bound public key. + #[inline] + #[must_use] + pub const fn key(&self) -> &'a RsaPublicKey { + self.key + } + + /// Return the verifier-owned JWT/JWS algorithm policy. + #[inline] + #[must_use] + pub const fn algorithm(&self) -> RsaJwtAlgorithm { + self.algorithm + } + + /// Verify a JWS signing input after matching the protected-header `alg`. + /// + /// `header_alg` must be the single decoded algorithm string produced by a + /// strict JOSE parser. The parser remains responsible for rejecting duplicate + /// header members, malformed JSON or UTF-8, invalid base64url, unsupported + /// critical parameters, and malformed compact serialization. + /// + /// # Errors + /// + /// Returns opaque [`VerificationError`] when `header_alg` is not the exact + /// canonical name of the bound algorithm or the RSA signature is invalid. + #[inline] + #[must_use = "signature verification must be checked; a dropped Result silently accepts a forged signature"] + pub fn verify(&self, header_alg: &str, message: &[u8], signature: &[u8]) -> Result<(), VerificationError> { + if header_alg != self.algorithm.as_str() { + return Err(VerificationError::new()); + } + self + .key + .verify_signature(self.algorithm.signature_profile(), message, signature) + } + + /// Verify with caller-owned RSA scratch after matching the protected-header `alg`. + /// + /// The same JOSE parsing requirements as [`Self::verify`] apply. + /// + /// # Errors + /// + /// Returns opaque [`VerificationError`] for an algorithm mismatch or invalid + /// signature. + #[inline] + #[must_use = "signature verification must be checked; a dropped Result silently accepts a forged signature"] + pub fn verify_with_scratch( + &self, + header_alg: &str, + message: &[u8], + signature: &[u8], + scratch: &mut RsaPublicScratch, + ) -> Result<(), VerificationError> { + if header_alg != self.algorithm.as_str() { + return Err(VerificationError::new()); + } + self + .key + .verify_signature_with_scratch(self.algorithm.signature_profile(), message, signature, scratch) + } +} + +impl fmt::Debug for RsaJwtVerifier<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RsaJwtVerifier") + .field("algorithm", &self.algorithm) + .finish_non_exhaustive() + } +} + impl fmt::Debug for RsaPrivateKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RsaPrivateKey") @@ -4592,6 +4653,16 @@ impl RsaPublicKey { RsaSignatureVerifier::new(self, profile) } + /// Return a JWT/JWS verifier bound to one locally selected algorithm. + /// + /// The returned verifier treats the peer's protected-header `alg` value as + /// an assertion to match, never as a signature-profile selector. + #[inline] + #[must_use] + pub const fn jwt_verifier(&self, algorithm: RsaJwtAlgorithm) -> RsaJwtVerifier<'_> { + RsaJwtVerifier::new(self, algorithm) + } + /// Build an RSA public key from canonical unsigned big-endian components /// with the modern default policy. /// @@ -5299,104 +5370,6 @@ impl RsaPublicKey { } } - /// Verify an RSA JWT/JWS signature only if the parsed JOSE `alg` matches the - /// locally configured expected `alg` and profile. - /// - /// `expected_alg` and `profile` must come from local verifier configuration. - /// `header_alg` is the value parsed from the peer-controlled JOSE header. A - /// header or profile mismatch is rejected before signature verification. - /// - /// # Errors - /// - /// Returns [`VerificationError`] if the algorithms differ, `profile` does - /// not match `expected_alg`, or the signature is invalid. - #[must_use = "signature verification must be checked; a dropped Result silently accepts a forged signature"] - pub fn verify_expected_jwt_alg( - &self, - expected_alg: &str, - header_alg: &str, - profile: RsaSignatureProfile, - message: &[u8], - signature: &[u8], - ) -> Result<(), VerificationError> { - if header_alg != expected_alg || !profile.matches_jwt_alg(expected_alg) { - return Err(VerificationError::new()); - } - self.verify_signature(profile, message, signature) - } - - /// Verify an RSA JWT/JWS signature with caller-owned scratch only if the - /// parsed JOSE `alg` matches the locally configured expected `alg` and - /// profile. - /// - /// # Errors - /// - /// Returns [`VerificationError`] for algorithm mismatch, unsupported expected - /// algorithms, or invalid signatures. - #[must_use = "signature verification must be checked; a dropped Result silently accepts a forged signature"] - pub fn verify_expected_jwt_alg_with_scratch( - &self, - expected_alg: &str, - header_alg: &str, - profile: RsaSignatureProfile, - message: &[u8], - signature: &[u8], - scratch: &mut RsaPublicScratch, - ) -> Result<(), VerificationError> { - if header_alg != expected_alg || !profile.matches_jwt_alg(expected_alg) { - return Err(VerificationError::new()); - } - self.verify_signature_with_scratch(profile, message, signature, scratch) - } - - /// Verify an RSA JWT/JWS signature by mapping the supplied JOSE `alg` directly. - /// - /// Compatibility helper only: prefer - /// [`verify_expected_jwt_alg`](Self::verify_expected_jwt_alg) when `alg` was - /// parsed from peer-controlled JOSE metadata. This is not a JWT, JWS, JOSE, - /// or JSON provider integration. - /// - /// `message` is the caller-constructed JWS Signing Input. This helper does - /// not parse JWT claims, JWS compact serialization, or JSON; it only enforces - /// the explicit RSA SHA-2 `alg` mapping before signature verification. - /// - /// # Errors - /// - /// Returns [`VerificationError`] if `alg` is not an accepted RSA SHA-2 JOSE - /// algorithm, or if the signature is invalid. - #[must_use = "signature verification must be checked; a dropped Result silently accepts a forged signature"] - pub fn verify_jwt_alg(&self, alg: &str, message: &[u8], signature: &[u8]) -> Result<(), VerificationError> { - let profile = RsaSignatureProfile::from_jwt_alg(alg).map_err(|_| VerificationError::new())?; - if !self.signature_profile_is_possible(profile) { - return Err(VerificationError::new()); - } - let mut scratch = self.public_scratch(); - self.verify_signature_with_scratch(profile, message, signature, &mut scratch) - } - - /// Verify an RSA JWT/JWS signature using caller-owned RSA scratch space by - /// mapping the supplied JOSE `alg` directly. - /// - /// Compatibility helper only: prefer - /// [`verify_expected_jwt_alg_with_scratch`](Self::verify_expected_jwt_alg_with_scratch) - /// when `alg` was parsed from peer-controlled JOSE metadata. - /// - /// # Errors - /// - /// Returns [`VerificationError`] if `alg` is unsupported or verification - /// fails. - #[must_use = "signature verification must be checked; a dropped Result silently accepts a forged signature"] - pub fn verify_jwt_alg_with_scratch( - &self, - alg: &str, - message: &[u8], - signature: &[u8], - scratch: &mut RsaPublicScratch, - ) -> Result<(), VerificationError> { - let profile = RsaSignatureProfile::from_jwt_alg(alg).map_err(|_| VerificationError::new())?; - self.verify_signature_with_scratch(profile, message, signature, scratch) - } - /// Verify an RSA COSE signature only if the parsed algorithm ID matches the /// locally configured expected algorithm ID and profile. /// @@ -12020,12 +11993,24 @@ f70203010001a3533051301d0603551d0e04160414fd0e576ce3f05b08884ad67ef3e8b4d39039c6 ) .unwrap(); - key.sign_jwt_alg("PS512", message, &mut signature).unwrap(); - key.public_key().verify_jwt_alg("PS512", message, &signature).unwrap(); + let jwt_algorithm = RsaJwtAlgorithm::Ps512; key - .sign_jwt_alg_with_scratch("PS512", message, &mut signature, &mut scratch) + .jwt_signer(jwt_algorithm) + .try_sign_into(message, &mut signature) + .unwrap(); + key + .public_key() + .jwt_verifier(jwt_algorithm) + .verify("PS512", message, &signature) + .unwrap(); + key + .sign_signature_with_scratch(jwt_algorithm.signature_profile(), message, &mut signature, &mut scratch) + .unwrap(); + key + .public_key() + .jwt_verifier(jwt_algorithm) + .verify("PS512", message, &signature) .unwrap(); - key.public_key().verify_jwt_alg("PS512", message, &signature).unwrap(); key.sign_cose_algorithm_id(-257, message, &mut signature).unwrap(); key @@ -12065,18 +12050,6 @@ f70203010001a3533051301d0603551d0e04160414fd0e576ce3f05b08884ad67ef3e8b4d39039c6 ); assert!(is_zero_unsigned_be(&signature)); signature.fill(0xa5); - assert_eq!( - key.sign_jwt_alg("HS256", message, &mut signature), - Err(RsaPrivateOpError::UnsupportedAlgorithm) - ); - assert!(is_zero_unsigned_be(&signature)); - signature.fill(0xa5); - assert_eq!( - key.sign_jwt_alg_with_scratch("HS256", message, &mut signature, &mut scratch), - Err(RsaPrivateOpError::UnsupportedAlgorithm) - ); - assert!(is_zero_unsigned_be(&signature)); - signature.fill(0xa5); assert_eq!( key.sign_cose_algorithm_id(-7, message, &mut signature), Err(RsaPrivateOpError::UnsupportedAlgorithm) @@ -13807,20 +13780,16 @@ ec34e8c72cc58fd5324fbe1ddd9714909caedfaa38706cfa66d9bc1026ba3ec1188092392a54a\ #[test] fn protocol_adapter_mappings_are_explicit_and_reject_confusion() { + assert_eq!(RsaJwtAlgorithm::Ps256.as_str(), "PS256"); assert_eq!( - RsaSignatureProfile::from_jwt_alg("PS256"), - Ok(RsaSignatureProfile::pss(RsaPssProfile::Sha256)) + RsaJwtAlgorithm::Ps256.signature_profile(), + RsaSignatureProfile::pss(RsaPssProfile::Sha256) ); + assert_eq!(RsaJwtAlgorithm::Rs512.as_str(), "RS512"); assert_eq!( - RsaSignatureProfile::from_jwt_alg("RS512"), - Ok(RsaSignatureProfile::pkcs1v15(RsaPkcs1v15Profile::Sha512)) + RsaJwtAlgorithm::Rs512.signature_profile(), + RsaSignatureProfile::pkcs1v15(RsaPkcs1v15Profile::Sha512) ); - for alg in ["none", "HS256", "ES256", "EdDSA", "RS1", "PS1", "rs256"] { - assert_eq!( - RsaSignatureProfile::from_jwt_alg(alg), - Err(RsaProtocolAlgorithmError::UnsupportedAlgorithm) - ); - } assert_eq!( RsaSignatureProfile::from_cose_algorithm_id(-37), diff --git a/src/lib.rs b/src/lib.rs index ec4ef55d..c5121a22 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -439,11 +439,11 @@ pub use auth::{Pbkdf2Error, Pbkdf2Params, Pbkdf2Sha256, Pbkdf2Sha512, Pbkdf2Veri pub use auth::{Poly1305, Poly1305OneTimeKey, Poly1305Tag}; #[cfg(feature = "rsa")] pub use auth::{ - RsaEncryptionError, RsaKeyError, RsaKeyGenerationContract, RsaKeyGenerationError, RsaOaepProfile, RsaPkcs1v15Profile, - RsaPrivateKey, RsaPrivateKeyParts, RsaPrivateOpError, RsaPrivateScratch, RsaProtocolAlgorithmError, RsaPssProfile, - RsaPublicExponent, RsaPublicExponentPolicy, RsaPublicKey, RsaPublicKeyPolicy, RsaPublicOpError, RsaPublicScratch, - RsaSignatureProfile, RsaSignatureSigner, RsaSignatureVerifier, RsaTlsSignatureSchemes, RsaX509PublicKey, - RsaX509PublicKeyAlgorithm, + RsaEncryptionError, RsaJwtAlgorithm, RsaJwtVerifier, RsaKeyError, RsaKeyGenerationContract, RsaKeyGenerationError, + RsaOaepProfile, RsaPkcs1v15Profile, RsaPrivateKey, RsaPrivateKeyParts, RsaPrivateOpError, RsaPrivateScratch, + RsaProtocolAlgorithmError, RsaPssProfile, RsaPublicExponent, RsaPublicExponentPolicy, RsaPublicKey, + RsaPublicKeyPolicy, RsaPublicOpError, RsaPublicScratch, RsaSignatureProfile, RsaSignatureSigner, + RsaSignatureVerifier, RsaTlsSignatureSchemes, RsaX509PublicKey, RsaX509PublicKeyAlgorithm, }; #[cfg(feature = "scrypt")] pub use auth::{Scrypt, ScryptError, ScryptParams}; diff --git a/tests/root_surface.rs b/tests/root_surface.rs index 22beb84f..0b89d17e 100644 --- a/tests/root_surface.rs +++ b/tests/root_surface.rs @@ -69,10 +69,11 @@ use rscrypto::{ use rscrypto::{Poly1305, Poly1305OneTimeKey, Poly1305Tag}; #[cfg(feature = "rsa")] use rscrypto::{ - RsaEncryptionError, RsaKeyError, RsaKeyGenerationError, RsaOaepProfile, RsaPkcs1v15Profile, RsaPrivateKey, - RsaPrivateKeyParts, RsaPrivateOpError, RsaPrivateScratch, RsaProtocolAlgorithmError, RsaPssProfile, - RsaPublicExponent, RsaPublicExponentPolicy, RsaPublicKey, RsaPublicKeyPolicy, RsaPublicOpError, RsaPublicScratch, - RsaSignatureProfile, RsaTlsSignatureSchemes, RsaX509PublicKey, RsaX509PublicKeyAlgorithm, + RsaEncryptionError, RsaJwtAlgorithm, RsaJwtVerifier, RsaKeyError, RsaKeyGenerationError, RsaOaepProfile, + RsaPkcs1v15Profile, RsaPrivateKey, RsaPrivateKeyParts, RsaPrivateOpError, RsaPrivateScratch, + RsaProtocolAlgorithmError, RsaPssProfile, RsaPublicExponent, RsaPublicExponentPolicy, RsaPublicKey, + RsaPublicKeyPolicy, RsaPublicOpError, RsaPublicScratch, RsaSignatureProfile, RsaTlsSignatureSchemes, + RsaX509PublicKey, RsaX509PublicKeyAlgorithm, }; use rscrypto::{VerificationError, ct}; #[cfg(feature = "x25519")] @@ -391,6 +392,7 @@ fn root_surface_rsa_exports_compile() { let _: Option = None; let _: Option = None; let _: Option = None; + let _: Option> = None; let _: Option = None; let _: Option> = None; let _: Option = None; @@ -630,29 +632,28 @@ fn root_surface_rsa_generated_key_end_to_end() { ) .unwrap(); - key.sign_jwt_alg("PS256", message, &mut signature).unwrap(); - public_key - .verify_expected_jwt_alg("PS256", "PS256", pss_sha256, message, &signature) - .unwrap(); - public_key - .verify_expected_jwt_alg_with_scratch("PS256", "PS256", pss_sha256, message, &signature, &mut public_scratch) - .unwrap(); key - .sign_jwt_alg_with_scratch("RS256", message, &mut signature, &mut private_scratch) + .jwt_signer(RsaJwtAlgorithm::Ps256) + .try_sign_into(message, &mut signature) .unwrap(); - public_key - .verify_expected_jwt_alg("RS256", "RS256", pkcs1v15_sha256, message, &signature) + let verifier = public_key.jwt_verifier(RsaJwtAlgorithm::Ps256); + verifier.verify("PS256", message, &signature).unwrap(); + verifier + .verify_with_scratch("PS256", message, &signature, &mut public_scratch) .unwrap(); - public_key - .verify_expected_jwt_alg_with_scratch( - "RS256", - "RS256", - pkcs1v15_sha256, + key + .sign_signature_with_scratch( + RsaJwtAlgorithm::Rs256.signature_profile(), message, - &signature, - &mut public_scratch, + &mut signature, + &mut private_scratch, ) .unwrap(); + let verifier = public_key.jwt_verifier(RsaJwtAlgorithm::Rs256); + verifier.verify("RS256", message, &signature).unwrap(); + verifier + .verify_with_scratch("RS256", message, &signature, &mut public_scratch) + .unwrap(); key.sign_cose_algorithm_id(-37, message, &mut signature).unwrap(); public_key diff --git a/tests/rsa_allocations.rs b/tests/rsa_allocations.rs index 0ca4e630..d75948f1 100644 --- a/tests/rsa_allocations.rs +++ b/tests/rsa_allocations.rs @@ -8,7 +8,7 @@ use std::alloc::System; use rsa::{BigUint, RsaPrivateKey as RustCryptoRsaPrivateKey, pkcs1::EncodeRsaPrivateKey}; use rscrypto::{ - RsaEncryptionError, RsaOaepProfile, RsaPkcs1v15Profile, RsaPrivateKey, RsaPssProfile, RsaPublicKey, + RsaEncryptionError, RsaJwtAlgorithm, RsaOaepProfile, RsaPkcs1v15Profile, RsaPrivateKey, RsaPssProfile, RsaPublicKey, RsaPublicKeyPolicy, RsaSignatureProfile, RsaX509PublicKey, }; @@ -408,7 +408,8 @@ fn reused_scratch_rsa_operations_do_not_allocate() { reset_allocations(); key - .verify_expected_jwt_alg_with_scratch("PS256", "PS256", pss_sha256, MESSAGE_PSS, &sig, &mut scratch) + .jwt_verifier(RsaJwtAlgorithm::Ps256) + .verify_with_scratch("PS256", MESSAGE_PSS, &sig, &mut scratch) .unwrap(); assert_eq!(allocation_count(), 0); @@ -469,7 +470,8 @@ fn reused_scratch_rsa_operations_do_not_allocate() { reset_allocations(); key - .verify_expected_jwt_alg_with_scratch("RS256", "RS256", pkcs1v15_sha256, MESSAGE_PKCS1V15, &sig, &mut scratch) + .jwt_verifier(RsaJwtAlgorithm::Rs256) + .verify_with_scratch("RS256", MESSAGE_PKCS1V15, &sig, &mut scratch) .unwrap(); assert_eq!(allocation_count(), 0); @@ -567,22 +569,6 @@ fn assert_private_protocol_signing_rejects_fail_before_entropy_allocation() { assert_eq!(allocation_count(), 0); assert!(signature.iter().all(|&byte| byte == 0)); - signature.fill(0xa5); - reset_allocations(); - assert!(key.sign_jwt_alg("HS256", message, &mut signature).is_err()); - assert_eq!(allocation_count(), 0); - assert!(signature.iter().all(|&byte| byte == 0)); - - signature.fill(0xa5); - reset_allocations(); - assert!( - key - .sign_jwt_alg_with_scratch("HS256", message, &mut signature, &mut scratch) - .is_err() - ); - assert_eq!(allocation_count(), 0); - assert!(signature.iter().all(|&byte| byte == 0)); - signature.fill(0xa5); reset_allocations(); assert!(key.sign_cose_algorithm_id(-7, message, &mut signature).is_err()); @@ -825,11 +811,21 @@ fn assert_one_shot_protocol_rejects_fail_before_scratch_allocation() { legacy_x509_public_key_from_spki(&pss_algorithm_spki_from_rsa_encryption_spki(&pss_spki())); reset_allocations(); - assert!(key.verify_jwt_alg("none", MESSAGE_PSS, &sig).is_err()); + assert!( + key + .jwt_verifier(RsaJwtAlgorithm::Ps256) + .verify("none", MESSAGE_PSS, &sig) + .is_err() + ); assert_eq!(allocation_count(), 0); reset_allocations(); - assert!(key.verify_jwt_alg("PS1", MESSAGE_PSS, &sig).is_err()); + assert!( + key + .jwt_verifier(RsaJwtAlgorithm::Ps256) + .verify("PS1", MESSAGE_PSS, &sig) + .is_err() + ); assert_eq!(allocation_count(), 0); reset_allocations(); diff --git a/tests/rsa_public_key.rs b/tests/rsa_public_key.rs index 5fc4f64c..87d72486 100644 --- a/tests/rsa_public_key.rs +++ b/tests/rsa_public_key.rs @@ -42,8 +42,8 @@ use rscrypto::auth::rsa::{ diag_rsa_verify_pss_encoded_with_scratch, }; use rscrypto::{ - RsaKeyError, RsaPkcs1v15Profile, RsaPrivateKey, RsaProtocolAlgorithmError, RsaPssProfile, RsaPublicKey, - RsaPublicKeyPolicy, RsaPublicOpError, RsaSignatureProfile, RsaTlsSignatureSchemes, RsaX509PublicKey, + RsaJwtAlgorithm, RsaKeyError, RsaPkcs1v15Profile, RsaPrivateKey, RsaProtocolAlgorithmError, RsaPssProfile, + RsaPublicKey, RsaPublicKeyPolicy, RsaPublicOpError, RsaSignatureProfile, RsaTlsSignatureSchemes, RsaX509PublicKey, RsaX509PublicKeyAlgorithm, VerificationError, }; @@ -2819,21 +2819,18 @@ fn protocol_verification_helpers_collapse_adapter_failures_to_opaque_error() { let out_of_range_pss_signature = public_key.modulus(); let malformed_fixed_width_pss_signature = modulus_minus_one(public_key); let mut public_scratch = public_key.public_scratch(); - assert_opaque_verification_failure(public_key.verify_jwt_alg("none", pss_fixture_message(), &pss_signature)); - assert_opaque_verification_failure(public_key.verify_jwt_alg("PS1", pss_fixture_message(), &pss_signature)); - assert_opaque_verification_failure(public_key.verify_jwt_alg("RS256", pss_fixture_message(), &pss_signature)); - assert_opaque_verification_failure(public_key.verify_jwt_alg("PS256", pss_fixture_message(), short_pss_signature)); - assert_opaque_verification_failure(public_key.verify_jwt_alg( - "PS256", - pss_fixture_message(), - out_of_range_pss_signature, - )); - assert_opaque_verification_failure(public_key.verify_jwt_alg( + let jwt_verifier = public_key.jwt_verifier(RsaJwtAlgorithm::Ps256); + assert_opaque_verification_failure(jwt_verifier.verify("none", pss_fixture_message(), &pss_signature)); + assert_opaque_verification_failure(jwt_verifier.verify("PS1", pss_fixture_message(), &pss_signature)); + assert_opaque_verification_failure(jwt_verifier.verify("RS256", pss_fixture_message(), &pss_signature)); + assert_opaque_verification_failure(jwt_verifier.verify("PS256", pss_fixture_message(), short_pss_signature)); + assert_opaque_verification_failure(jwt_verifier.verify("PS256", pss_fixture_message(), out_of_range_pss_signature)); + assert_opaque_verification_failure(jwt_verifier.verify( "PS256", pss_fixture_message(), &malformed_fixed_width_pss_signature, )); - assert_opaque_verification_failure(public_key.verify_jwt_alg_with_scratch( + assert_opaque_verification_failure(jwt_verifier.verify_with_scratch( "PS256", pss_fixture_message(), &tampered_pss_signature, @@ -3001,7 +2998,7 @@ fn protocol_verification_helpers_collapse_adapter_failures_to_opaque_error() { let short_pkcs1_signature = &pkcs1_signature[..pkcs1_signature.len().strict_sub(1)]; let out_of_range_pkcs1_signature = pkcs1_key.public_key().modulus(); let malformed_fixed_width_pkcs1_signature = modulus_minus_one(pkcs1_key.public_key()); - assert_opaque_verification_failure(pkcs1_key.public_key().verify_jwt_alg( + assert_opaque_verification_failure(pkcs1_key.public_key().jwt_verifier(RsaJwtAlgorithm::Rs256).verify( "RS256", pkcs1v15_fixture_message(), &malformed_fixed_width_pkcs1_signature, @@ -3941,14 +3938,42 @@ fn protocol_algorithm_mappers_are_explicit_and_fail_closed() { RsaSignatureProfile::from_tls_certificate_signature_scheme(0x0601), Ok(RsaSignatureProfile::pkcs1v15(RsaPkcs1v15Profile::Sha512)) ); - assert_eq!( - RsaSignatureProfile::from_jwt_alg("PS256"), - Ok(RsaSignatureProfile::pss(RsaPssProfile::Sha256)) - ); - assert_eq!( - RsaSignatureProfile::from_jwt_alg("RS512"), - Ok(RsaSignatureProfile::pkcs1v15(RsaPkcs1v15Profile::Sha512)) - ); + for (algorithm, name, profile) in [ + ( + RsaJwtAlgorithm::Rs256, + "RS256", + RsaSignatureProfile::pkcs1v15(RsaPkcs1v15Profile::Sha256), + ), + ( + RsaJwtAlgorithm::Rs384, + "RS384", + RsaSignatureProfile::pkcs1v15(RsaPkcs1v15Profile::Sha384), + ), + ( + RsaJwtAlgorithm::Rs512, + "RS512", + RsaSignatureProfile::pkcs1v15(RsaPkcs1v15Profile::Sha512), + ), + ( + RsaJwtAlgorithm::Ps256, + "PS256", + RsaSignatureProfile::pss(RsaPssProfile::Sha256), + ), + ( + RsaJwtAlgorithm::Ps384, + "PS384", + RsaSignatureProfile::pss(RsaPssProfile::Sha384), + ), + ( + RsaJwtAlgorithm::Ps512, + "PS512", + RsaSignatureProfile::pss(RsaPssProfile::Sha512), + ), + ] { + assert_eq!(algorithm.as_str(), name); + assert_eq!(algorithm.to_string(), name); + assert_eq!(algorithm.signature_profile(), profile); + } assert_eq!( RsaSignatureProfile::from_cose_algorithm_id(-37), Ok(RsaSignatureProfile::pss(RsaPssProfile::Sha256)) @@ -3972,12 +3997,6 @@ fn protocol_algorithm_mappers_are_explicit_and_fail_closed() { Err(RsaProtocolAlgorithmError::UnsupportedAlgorithm) ); } - for jwt_alg in ["none", "HS256", "ES256", "EdDSA", "RS1", "PS1", "rs256", "RS256 "] { - assert_eq!( - RsaSignatureProfile::from_jwt_alg(jwt_alg), - Err(RsaProtocolAlgorithmError::UnsupportedAlgorithm) - ); - } for cose_algorithm in [0, 1, -7, -65535, -1, 0x7fff_ffff, i64::MAX - 1] { assert_eq!( RsaSignatureProfile::from_cose_algorithm_id(cose_algorithm), @@ -4007,12 +4026,6 @@ fn provider_facing_legacy_algorithm_rejections_are_typed() { assert_unsupported_protocol_algorithm(RsaSignatureProfile::from_tls_certificate_signature_scheme(tls_scheme)); } - for jwt_alg in [ - "none", "HS256", "ES256", "EdDSA", "RS1", "PS1", "rs256", "PS256\0", "RS256 ", - ] { - assert_unsupported_protocol_algorithm(RsaSignatureProfile::from_jwt_alg(jwt_alg)); - } - for cose_algorithm in [0, 1, -7, -65535, -65_536, i64::MIN, -1, i64::MAX - 1, i64::MAX] { assert_unsupported_protocol_algorithm(RsaSignatureProfile::from_cose_algorithm_id(cose_algorithm)); } @@ -4052,7 +4065,7 @@ fn protocol_mapped_profiles_verify_and_reject_algorithm_confusion() { assert!( pss_key .verify_signature( - RsaSignatureProfile::from_jwt_alg("PS256").unwrap(), + RsaJwtAlgorithm::Ps256.signature_profile(), pss_fixture_message(), &pss_sig, ) @@ -4081,7 +4094,7 @@ fn protocol_mapped_profiles_verify_and_reject_algorithm_confusion() { assert!( pkcs1_key .verify_signature( - RsaSignatureProfile::from_jwt_alg("RS256").unwrap(), + RsaJwtAlgorithm::Rs256.signature_profile(), pkcs1v15_fixture_message(), &pkcs1_sig, ) @@ -4098,30 +4111,42 @@ fn protocol_mapped_profiles_verify_and_reject_algorithm_confusion() { } #[test] -fn jwt_and_cose_verification_helpers_reject_algorithm_confusion() { +fn jwt_policy_and_cose_helpers_reject_algorithm_confusion() { let pss_key = legacy_public_key_from_spki(&pss_fixture_public_key()); let pss_sig = pss_fixture_signature_sha256(); let pss_profile = RsaSignatureProfile::pss(RsaPssProfile::Sha256); let pkcs1_profile = RsaSignatureProfile::pkcs1v15(RsaPkcs1v15Profile::Sha256); let mut scratch = pss_key.public_scratch(); + let pss_jwt = pss_key.jwt_verifier(RsaJwtAlgorithm::Ps256); + assert_eq!(pss_jwt.algorithm(), RsaJwtAlgorithm::Ps256); + assert!(core::ptr::eq(pss_jwt.key(), &pss_key)); + assert!(pss_jwt.verify("PS256", pss_fixture_message(), &pss_sig).is_ok()); assert!( - pss_key - .verify_expected_jwt_alg("PS256", "PS256", pss_profile, pss_fixture_message(), &pss_sig) - .is_ok() - ); - assert!( - pss_key - .verify_expected_jwt_alg_with_scratch( - "PS256", - "PS256", - pss_profile, - pss_fixture_message(), - &pss_sig, - &mut scratch, - ) + pss_jwt + .verify_with_scratch("PS256", pss_fixture_message(), &pss_sig, &mut scratch) .is_ok() ); + for header_alg in [ + "", + "none", + "HS256", + "ES256", + "EdDSA", + "RS256", + "PS1", + "ps256", + "Ps256", + "PS256\0", + "PS256 ", + "\"PS256\"", + ] { + assert_eq!( + pss_jwt.verify(header_alg, pss_fixture_message(), &pss_sig), + Err(VerificationError::new()), + "accepted noncanonical or mismatched JWT alg {header_alg:?}", + ); + } assert!( pss_key .verify_expected_cose_algorithm_id(-37, -37, pss_profile, pss_fixture_message(), &pss_sig) @@ -4140,12 +4165,9 @@ fn jwt_and_cose_verification_helpers_reject_algorithm_confusion() { .is_ok() ); for result in [ - pss_key.verify_expected_jwt_alg("PS256", "RS256", pss_profile, pss_fixture_message(), &pss_sig), - pss_key.verify_expected_jwt_alg("PS256", "PS256", pkcs1_profile, pss_fixture_message(), &pss_sig), - pss_key.verify_jwt_alg("RS256", pss_fixture_message(), &pss_sig), - pss_key.verify_jwt_alg("none", pss_fixture_message(), &pss_sig), - pss_key.verify_jwt_alg("HS256", pss_fixture_message(), &pss_sig), - pss_key.verify_jwt_alg("rs256", pss_fixture_message(), &pss_sig), + pss_key + .jwt_verifier(RsaJwtAlgorithm::Rs256) + .verify("RS256", pss_fixture_message(), &pss_sig), pss_key.verify_expected_cose_algorithm_id(-37, -257, pss_profile, pss_fixture_message(), &pss_sig), pss_key.verify_expected_cose_algorithm_id(-37, -37, pkcs1_profile, pss_fixture_message(), &pss_sig), pss_key.verify_cose_algorithm_id(-257, pss_fixture_message(), &pss_sig), @@ -4157,22 +4179,16 @@ fn jwt_and_cose_verification_helpers_reject_algorithm_confusion() { let pkcs1_key = legacy_public_key_from_spki(&pkcs1v15_fixture_public_key()); let pkcs1_sig = pkcs1v15_fixture_signature_sha256(); let mut scratch = pkcs1_key.public_scratch(); + let pkcs1_jwt = pkcs1_key.jwt_verifier(RsaJwtAlgorithm::Rs256); assert!( - pkcs1_key - .verify_expected_jwt_alg("RS256", "RS256", pkcs1_profile, pkcs1v15_fixture_message(), &pkcs1_sig) + pkcs1_jwt + .verify("RS256", pkcs1v15_fixture_message(), &pkcs1_sig) .is_ok() ); assert!( - pkcs1_key - .verify_expected_jwt_alg_with_scratch( - "RS256", - "RS256", - pkcs1_profile, - pkcs1v15_fixture_message(), - &pkcs1_sig, - &mut scratch, - ) + pkcs1_jwt + .verify_with_scratch("RS256", pkcs1v15_fixture_message(), &pkcs1_sig, &mut scratch) .is_ok() ); assert!( @@ -4193,11 +4209,10 @@ fn jwt_and_cose_verification_helpers_reject_algorithm_confusion() { .is_ok() ); for result in [ - pkcs1_key.verify_expected_jwt_alg("RS256", "PS256", pkcs1_profile, pkcs1v15_fixture_message(), &pkcs1_sig), - pkcs1_key.verify_expected_jwt_alg("RS256", "RS256", pss_profile, pkcs1v15_fixture_message(), &pkcs1_sig), - pkcs1_key.verify_jwt_alg("PS256", pkcs1v15_fixture_message(), &pkcs1_sig), - pkcs1_key.verify_jwt_alg("RS1", pkcs1v15_fixture_message(), &pkcs1_sig), - pkcs1_key.verify_jwt_alg("ES256", pkcs1v15_fixture_message(), &pkcs1_sig), + pkcs1_key + .jwt_verifier(RsaJwtAlgorithm::Ps256) + .verify("PS256", pkcs1v15_fixture_message(), &pkcs1_sig), + pkcs1_jwt.verify("PS256", pkcs1v15_fixture_message(), &pkcs1_sig), pkcs1_key.verify_expected_cose_algorithm_id(-257, -37, pkcs1_profile, pkcs1v15_fixture_message(), &pkcs1_sig), pkcs1_key.verify_expected_cose_algorithm_id(-257, -257, pss_profile, pkcs1v15_fixture_message(), &pkcs1_sig), pkcs1_key.verify_cose_algorithm_id(-37, pkcs1v15_fixture_message(), &pkcs1_sig),