Skip to main content

identity_kit/jose/
verifier.rs

1//! `JwsVerifier` and the pure-Rust `DefaultVerifier`.
2
3use super::{Jwk, JwsAlgorithm, VerificationError};
4
5/// Verifies JWS signatures.
6///
7/// The verifier is the crate's crypto extension point: [`DefaultVerifier`] implements the
8/// algorithms with RustCrypto, and an application can substitute an HSM-backed or FIPS-validated
9/// one. Implementations take the signature in the JWS form of the algorithm: 64 bytes for EdDSA,
10/// raw `r || s` (not DER) for ECDSA, the modulus-sized octet string for RSA.
11pub trait JwsVerifier: Send + Sync {
12    /// Verifies that `signature` over `signing_input` was made with the private counterpart of
13    /// `key` under `alg`.
14    ///
15    /// # Errors
16    ///
17    /// A [`VerificationError`] when the key, the signature or the algorithm is unusable, and
18    /// [`VerificationError::InvalidSignature`] when everything is well-formed but the signature
19    /// does not verify.
20    fn verify(
21        &self,
22        alg: JwsAlgorithm,
23        signing_input: &[u8],
24        signature: &[u8],
25        key: &Jwk,
26    ) -> Result<(), VerificationError>;
27}
28
29impl<T: JwsVerifier + ?Sized> JwsVerifier for &T {
30    fn verify(
31        &self,
32        alg: JwsAlgorithm,
33        signing_input: &[u8],
34        signature: &[u8],
35        key: &Jwk,
36    ) -> Result<(), VerificationError> {
37        (**self).verify(alg, signing_input, signature, key)
38    }
39}
40
41impl<T: JwsVerifier + ?Sized> JwsVerifier for Box<T> {
42    fn verify(
43        &self,
44        alg: JwsAlgorithm,
45        signing_input: &[u8],
46        signature: &[u8],
47        key: &Jwk,
48    ) -> Result<(), VerificationError> {
49        (**self).verify(alg, signing_input, signature, key)
50    }
51}
52
53impl<T: JwsVerifier + ?Sized> JwsVerifier for std::sync::Arc<T> {
54    fn verify(
55        &self,
56        alg: JwsAlgorithm,
57        signing_input: &[u8],
58        signature: &[u8],
59        key: &Jwk,
60    ) -> Result<(), VerificationError> {
61        (**self).verify(alg, signing_input, signature, key)
62    }
63}
64
65/// A [`JwsVerifier`] over RustCrypto: `ed25519-dalek` for EdDSA, `p256` for ES256, `k256` for
66/// ES256K (feature `secp256k1`) and `rsa` for RS256 and PS256 (feature `rsa`).
67///
68/// ES384 is in the [`JwsAlgorithm`] vocabulary but not implemented here; verifying it returns
69/// [`VerificationError::UnsupportedAlgorithm`].
70///
71/// Before the signature is checked, the key's own restrictions are: a key whose `alg` names
72/// another algorithm, whose `use` is not `sig` or whose `key_ops` lacks `verify` is rejected.
73///
74/// # ECDSA malleability
75///
76/// When an ECDSA signature `(r, s)` verifies, so does `(r, n - s)`, where `n` is the group order.
77/// RFC 7518 §3.4 asks for no canonical `s`, and ES256 verification accepts both, so anyone
78/// holding an ES256 JWS can produce a second, different compact serialization of it that
79/// verifies just the same. ES256K accepts only the lower `s`, as `k256` and most secp256k1
80/// verifiers do ([`normalize_ecdsa_signature`](super::normalize_ecdsa_signature) brings a
81/// signer's output into that form).
82///
83/// The signature still means the same: the signer did sign those claims. What it rules out is
84/// treating a token's bytes as unique. An application that keeps a replay cache or deduplicates
85/// tokens identifies a token by its claims (`jti`, `iss`, `nonce`), not by its compact
86/// serialization or a hash of it.
87///
88/// # Examples
89///
90/// ```
91/// use identity_kit::jose::{DefaultVerifier, Jwk, JwsAlgorithm, JwsVerifier};
92///
93/// // RFC 8037 Appendix A.4 and A.5.
94/// let key: Jwk = serde_json::from_str(
95///     r#"{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}"#,
96/// )?;
97/// let token = "eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc.\
98///              hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg";
99/// let jws = identity_kit::jose::CompactJws::decode(token)?;
100/// DefaultVerifier.verify(JwsAlgorithm::EdDSA, jws.signing_input(), jws.signature(), &key)?;
101/// # Ok::<(), Box<dyn std::error::Error>>(())
102/// ```
103#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
104pub struct DefaultVerifier;
105
106impl JwsVerifier for DefaultVerifier {
107    fn verify(
108        &self,
109        alg: JwsAlgorithm,
110        signing_input: &[u8],
111        signature: &[u8],
112        key: &Jwk,
113    ) -> Result<(), VerificationError> {
114        check_key_restrictions(alg, key)?;
115        match alg {
116            JwsAlgorithm::EdDSA => {
117                let verifying_key = key.ed25519_verifying_key()?;
118                let signature = ed25519_dalek::Signature::from_bytes(fixed_size::<64>(signature)?);
119                verifying_key
120                    .verify_strict(signing_input, &signature)
121                    .map_err(|_| VerificationError::InvalidSignature)
122            }
123            JwsAlgorithm::ES256 => {
124                use p256::ecdsa::signature::Verifier as _;
125                let verifying_key = p256::ecdsa::VerifyingKey::from(&key.p256_public_key()?);
126                let signature = p256::ecdsa::Signature::from_slice(ecdsa_raw::<64>(signature)?)
127                    .map_err(|_| VerificationError::InvalidSignature)?;
128                verifying_key
129                    .verify(signing_input, &signature)
130                    .map_err(|_| VerificationError::InvalidSignature)
131            }
132            #[cfg(feature = "secp256k1")]
133            JwsAlgorithm::ES256K => {
134                use k256::ecdsa::signature::Verifier as _;
135                let verifying_key = k256::ecdsa::VerifyingKey::from(&key.secp256k1_public_key()?);
136                let signature = k256::ecdsa::Signature::from_slice(ecdsa_raw::<64>(signature)?)
137                    .map_err(|_| VerificationError::InvalidSignature)?;
138                verifying_key
139                    .verify(signing_input, &signature)
140                    .map_err(|_| VerificationError::InvalidSignature)
141            }
142            #[cfg(feature = "rsa")]
143            JwsAlgorithm::RS256 => {
144                use rsa::signature::Verifier as _;
145                let public_key = key.rsa_public_key()?;
146                let signature =
147                    rsa::pkcs1v15::Signature::try_from(rsa_sized(&public_key, signature)?)
148                        .map_err(|_| VerificationError::InvalidSignature)?;
149                rsa::pkcs1v15::VerifyingKey::<sha2::Sha256>::new(public_key)
150                    .verify(signing_input, &signature)
151                    .map_err(|_| VerificationError::InvalidSignature)
152            }
153            #[cfg(feature = "rsa")]
154            JwsAlgorithm::PS256 => {
155                use rsa::signature::Verifier as _;
156                let public_key = key.rsa_public_key()?;
157                let signature = rsa::pss::Signature::try_from(rsa_sized(&public_key, signature)?)
158                    .map_err(|_| VerificationError::InvalidSignature)?;
159                rsa::pss::VerifyingKey::<sha2::Sha256>::new(public_key)
160                    .verify(signing_input, &signature)
161                    .map_err(|_| VerificationError::InvalidSignature)
162            }
163            other => Err(VerificationError::UnsupportedAlgorithm(other)),
164        }
165    }
166}
167
168/// Rejects a key whose `alg`, `use` or `key_ops` excludes verifying with `alg`.
169fn check_key_restrictions(alg: JwsAlgorithm, key: &Jwk) -> Result<(), VerificationError> {
170    match key.alg() {
171        Some(key_alg) if key_alg != alg.as_str() => {
172            return Err(VerificationError::AlgorithmMismatch {
173                alg,
174                key_alg: key_alg.to_owned(),
175            });
176        }
177        _ => {}
178    }
179    if key.key_use().is_some_and(|key_use| key_use != "sig") {
180        return Err(VerificationError::KeyUse("`use` is not `sig`"));
181    }
182    if key
183        .key_ops()
184        .is_some_and(|key_ops| !key_ops.iter().any(|op| op == "verify"))
185    {
186        return Err(VerificationError::KeyUse("`key_ops` lacks `verify`"));
187    }
188    Ok(())
189}
190
191/// `signature` as an array of exactly `N` bytes.
192fn fixed_size<const N: usize>(signature: &[u8]) -> Result<&[u8; N], VerificationError> {
193    signature
194        .try_into()
195        .map_err(|_| VerificationError::InvalidSignatureLength {
196            expected: N,
197            actual: signature.len(),
198        })
199}
200
201/// `signature` as the octet string of exactly the modulus length that RFC 7518 §3.3 and §3.5
202/// require. An RSA signature is an integer, so without this check a shorter or longer encoding
203/// of the same value (a leading zero byte dropped or added) would verify as well.
204#[cfg(feature = "rsa")]
205fn rsa_sized<'a>(
206    key: &rsa::RsaPublicKey,
207    signature: &'a [u8],
208) -> Result<&'a [u8], VerificationError> {
209    use rsa::traits::PublicKeyParts as _;
210    let expected = key.size();
211    if signature.len() != expected {
212        return Err(VerificationError::InvalidSignatureLength {
213            expected,
214            actual: signature.len(),
215        });
216    }
217    Ok(signature)
218}
219
220/// `signature` as a raw `r || s` ECDSA signature of exactly `N` bytes, with a dedicated error for
221/// a DER-encoded one.
222fn ecdsa_raw<const N: usize>(signature: &[u8]) -> Result<&[u8; N], VerificationError> {
223    // A DER ECDSA-Sig-Value is a SEQUENCE whose length (N + 6 to N + 8 bytes for the curves here)
224    // never equals the raw length, so a 0x30 tag on an input of the wrong length is DER.
225    if signature.len() != N && signature.first() == Some(&0x30) {
226        return Err(VerificationError::DerEncodedSignature);
227    }
228    fixed_size::<N>(signature)
229}