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/// # Examples
75///
76/// ```
77/// use identity_kit::jose::{DefaultVerifier, Jwk, JwsAlgorithm, JwsVerifier};
78///
79/// // RFC 8037 Appendix A.4 and A.5.
80/// let key: Jwk = serde_json::from_str(
81///     r#"{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}"#,
82/// )?;
83/// let token = "eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc.\
84///              hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg";
85/// let jws = identity_kit::jose::CompactJws::decode(token)?;
86/// DefaultVerifier.verify(JwsAlgorithm::EdDSA, jws.signing_input(), jws.signature(), &key)?;
87/// # Ok::<(), Box<dyn std::error::Error>>(())
88/// ```
89#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
90pub struct DefaultVerifier;
91
92impl JwsVerifier for DefaultVerifier {
93    fn verify(
94        &self,
95        alg: JwsAlgorithm,
96        signing_input: &[u8],
97        signature: &[u8],
98        key: &Jwk,
99    ) -> Result<(), VerificationError> {
100        check_key_restrictions(alg, key)?;
101        match alg {
102            JwsAlgorithm::EdDSA => {
103                let verifying_key = key.ed25519_verifying_key()?;
104                let signature = ed25519_dalek::Signature::from_bytes(fixed_size::<64>(signature)?);
105                verifying_key
106                    .verify_strict(signing_input, &signature)
107                    .map_err(|_| VerificationError::InvalidSignature)
108            }
109            JwsAlgorithm::ES256 => {
110                use p256::ecdsa::signature::Verifier as _;
111                let verifying_key = p256::ecdsa::VerifyingKey::from(&key.p256_public_key()?);
112                let signature = p256::ecdsa::Signature::from_slice(ecdsa_raw::<64>(signature)?)
113                    .map_err(|_| VerificationError::InvalidSignature)?;
114                verifying_key
115                    .verify(signing_input, &signature)
116                    .map_err(|_| VerificationError::InvalidSignature)
117            }
118            #[cfg(feature = "secp256k1")]
119            JwsAlgorithm::ES256K => {
120                use k256::ecdsa::signature::Verifier as _;
121                let verifying_key = k256::ecdsa::VerifyingKey::from(&key.secp256k1_public_key()?);
122                let signature = k256::ecdsa::Signature::from_slice(ecdsa_raw::<64>(signature)?)
123                    .map_err(|_| VerificationError::InvalidSignature)?;
124                verifying_key
125                    .verify(signing_input, &signature)
126                    .map_err(|_| VerificationError::InvalidSignature)
127            }
128            #[cfg(feature = "rsa")]
129            JwsAlgorithm::RS256 => {
130                use rsa::signature::Verifier as _;
131                let public_key = key.rsa_public_key()?;
132                let signature =
133                    rsa::pkcs1v15::Signature::try_from(rsa_sized(&public_key, signature)?)
134                        .map_err(|_| VerificationError::InvalidSignature)?;
135                rsa::pkcs1v15::VerifyingKey::<sha2::Sha256>::new(public_key)
136                    .verify(signing_input, &signature)
137                    .map_err(|_| VerificationError::InvalidSignature)
138            }
139            #[cfg(feature = "rsa")]
140            JwsAlgorithm::PS256 => {
141                use rsa::signature::Verifier as _;
142                let public_key = key.rsa_public_key()?;
143                let signature = rsa::pss::Signature::try_from(rsa_sized(&public_key, signature)?)
144                    .map_err(|_| VerificationError::InvalidSignature)?;
145                rsa::pss::VerifyingKey::<sha2::Sha256>::new(public_key)
146                    .verify(signing_input, &signature)
147                    .map_err(|_| VerificationError::InvalidSignature)
148            }
149            other => Err(VerificationError::UnsupportedAlgorithm(other)),
150        }
151    }
152}
153
154/// Rejects a key whose `alg`, `use` or `key_ops` excludes verifying with `alg`.
155fn check_key_restrictions(alg: JwsAlgorithm, key: &Jwk) -> Result<(), VerificationError> {
156    match key.alg() {
157        Some(key_alg) if key_alg != alg.as_str() => {
158            return Err(VerificationError::AlgorithmMismatch {
159                alg,
160                key_alg: key_alg.to_owned(),
161            });
162        }
163        _ => {}
164    }
165    if key.key_use().is_some_and(|key_use| key_use != "sig") {
166        return Err(VerificationError::KeyUse("`use` is not `sig`"));
167    }
168    if key
169        .key_ops()
170        .is_some_and(|key_ops| !key_ops.iter().any(|op| op == "verify"))
171    {
172        return Err(VerificationError::KeyUse("`key_ops` lacks `verify`"));
173    }
174    Ok(())
175}
176
177/// `signature` as an array of exactly `N` bytes.
178fn fixed_size<const N: usize>(signature: &[u8]) -> Result<&[u8; N], VerificationError> {
179    signature
180        .try_into()
181        .map_err(|_| VerificationError::InvalidSignatureLength {
182            expected: N,
183            actual: signature.len(),
184        })
185}
186
187/// `signature` as the octet string of exactly the modulus length that RFC 7518 §3.3 and §3.5
188/// require. An RSA signature is an integer, so without this check a shorter or longer encoding
189/// of the same value (a leading zero byte dropped or added) would verify as well.
190#[cfg(feature = "rsa")]
191fn rsa_sized<'a>(
192    key: &rsa::RsaPublicKey,
193    signature: &'a [u8],
194) -> Result<&'a [u8], VerificationError> {
195    use rsa::traits::PublicKeyParts as _;
196    let expected = key.size();
197    if signature.len() != expected {
198        return Err(VerificationError::InvalidSignatureLength {
199            expected,
200            actual: signature.len(),
201        });
202    }
203    Ok(signature)
204}
205
206/// `signature` as a raw `r || s` ECDSA signature of exactly `N` bytes, with a dedicated error for
207/// a DER-encoded one.
208fn ecdsa_raw<const N: usize>(signature: &[u8]) -> Result<&[u8; N], VerificationError> {
209    // A DER ECDSA-Sig-Value is a SEQUENCE whose length (N + 6 to N + 8 bytes for the curves here)
210    // never equals the raw length, so a 0x30 tag on an input of the wrong length is DER.
211    if signature.len() != N && signature.first() == Some(&0x30) {
212        return Err(VerificationError::DerEncodedSignature);
213    }
214    fixed_size::<N>(signature)
215}