Skip to main content

asjeeves_encryption/
signature_verification.rs

1use std::borrow::Cow;
2
3use json_web_key::prelude::*;
4use json_web_tolkien::prelude::*;
5use rsa::pkcs1v15::{Signature, VerifyingKey};
6use rsa::sha2::Sha256;
7use rsa::signature::Verifier;
8use rsa::{BigUint, RsaPublicKey};
9
10use crate::Error;
11
12pub trait SignatureVerification {
13    fn verify_signature(&self, jws: Jws) -> Result<(), Error>;
14}
15
16impl SignatureVerification for JsonWebKey {
17    fn verify_signature(&self, jws: Jws) -> Result<(), Error> {
18        let JsonWebKey::RS256(key) = self else {
19            return Err(Error::UnsupportedAlgorithm);
20        };
21
22        let exponent = BigUint::from_bytes_be(key.exponent.as_ref());
23        let modulus = BigUint::from_bytes_be(key.modulus.as_ref());
24
25        let key =
26            RsaPublicKey::new(modulus, exponent).map_err(|source| Error::KeyGenError { source })?;
27
28        let key = VerifyingKey::<Sha256>::new(key);
29
30        let signature: Cow<[u8]> = jws.signature();
31        let signature = Signature::try_from(signature.as_ref())?;
32
33        let token: Cow<str> = jws.encoded_token();
34        let token: &[u8] = token.as_bytes();
35
36        key.verify(token, &signature)?;
37
38        Ok(())
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45    use crate::seed::Seed;
46    use crate::web_key::WebKey;
47    use rsa::RsaPrivateKey;
48    use rsa::pkcs1v15::SigningKey;
49    use rsa::sha2::Sha256;
50    use rsa::signature::{SignatureEncoding, Signer};
51    use serde::{Deserialize, Serialize};
52
53    #[derive(Deserialize, Serialize)]
54    struct Header {
55        pub alg: Box<str>,
56        pub kid: Box<str>,
57    }
58
59    #[derive(Deserialize, Serialize)]
60    struct Claims {
61        pub iat: u64,
62        pub name: Box<str>,
63        pub sub: Box<str>,
64    }
65
66    fn create_test_key_and_jwk() -> (RsaPrivateKey, JsonWebKey) {
67        let seed = Seed::from(1);
68        let mut rng = seed.rng();
69        let web_key = WebKey::generate(&mut rng).expect("Failed to generate web key");
70        let jwk = web_key.to_json_web_key();
71
72        // Extract private key for signing
73        let private_key = {
74            let seed = Seed::from(1);
75            let mut rng = seed.rng();
76            RsaPrivateKey::new(&mut rng, 2048).expect("Failed to generate RSA key")
77        };
78
79        (private_key, jwk)
80    }
81
82    fn create_signed_jws(private_key: &RsaPrivateKey, claims: Claims) -> Jws {
83        let header = Header {
84            alg: "RS256".into(),
85            kid: "test-key-id".into(),
86        };
87
88        let token: Jwt<Header, Claims> = Jwt::new(header, claims);
89
90        let token: String = serde_json::to_string(&token).expect("jwt to serialize to a string");
91
92        let signing_key = SigningKey::<Sha256>::new(private_key.clone());
93        let signature = signing_key.sign(token.as_bytes());
94
95        Jws::new(token.as_str(), signature.to_bytes().as_ref())
96    }
97
98    #[test]
99    fn test_verify_valid_signature() {
100        let (private_key, jwk) = create_test_key_and_jwk();
101        let payload = Claims {
102            sub: "1234567890".into(),
103            name: "Test User".into(),
104            iat: 1516239022,
105        };
106        let jws = create_signed_jws(&private_key, payload);
107
108        let result = jwk.verify_signature(jws);
109        assert!(result.is_ok(), "Valid signature should verify successfully");
110    }
111
112    #[test]
113    fn test_verify_invalid_signature() {
114        let (_, jwk) = create_test_key_and_jwk();
115
116        // Create a different key for signing
117        let seed = Seed::from(2);
118        let mut rng = seed.rng();
119        let wrong_private_key =
120            RsaPrivateKey::new(&mut rng, 2048).expect("Failed to generate wrong key");
121
122        let payload = Claims {
123            sub: "1234567890".into(),
124            name: "Test User".into(),
125            iat: 1516239022,
126        };
127
128        let jws = create_signed_jws(&wrong_private_key, payload);
129
130        let result = jwk.verify_signature(jws);
131        assert!(
132            result.is_err(),
133            "Invalid signature should fail verification"
134        );
135
136        match result {
137            Err(Error::InvalidSignature { .. }) => {}
138            _ => panic!("Expected InvalidSignature error"),
139        }
140    }
141
142    #[test]
143    fn test_verify_tampered_payload() {
144        let (private_key, jwk) = create_test_key_and_jwk();
145
146        let original_payload = Claims {
147            sub: "1234567890".into(),
148            name: "Test User".into(),
149            iat: 1516239022,
150        };
151
152        let jws = create_signed_jws(&private_key, original_payload);
153        let signature: Cow<[u8]> = jws.signature();
154
155        // Manually tamper with the JWS by modifying the payload part
156
157        let tampered_payload = Claims {
158            sub: "9999999999".into(),
159            name: "Hacker".into(),
160            iat: 1516239022,
161        };
162
163        let tampered_jws = create_signed_jws(&private_key, tampered_payload);
164
165        let tampered_token = tampered_jws.encoded_token();
166
167        let tampered_jws = Jws::new(tampered_token.as_ref(), signature.as_ref());
168
169        let result = jwk.verify_signature(tampered_jws);
170        assert!(result.is_err(), "Tampered payload should fail verification");
171
172        match result {
173            Err(Error::InvalidSignature { .. }) => {}
174            _ => panic!("Expected InvalidSignature error for tampered payload"),
175        }
176    }
177}