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        let jws = Jws::new(token.as_str(), signature.to_bytes().as_ref());
96
97        jws
98    }
99
100    #[test]
101    fn test_verify_valid_signature() {
102        let (private_key, jwk) = create_test_key_and_jwk();
103        let payload = Claims {
104            sub: "1234567890".into(),
105            name: "Test User".into(),
106            iat: 1516239022,
107        };
108        let jws = create_signed_jws(&private_key, payload);
109
110        let result = jwk.verify_signature(jws);
111        assert!(result.is_ok(), "Valid signature should verify successfully");
112    }
113
114    #[test]
115    fn test_verify_invalid_signature() {
116        let (_, jwk) = create_test_key_and_jwk();
117
118        // Create a different key for signing
119        let seed = Seed::from(2);
120        let mut rng = seed.rng();
121        let wrong_private_key =
122            RsaPrivateKey::new(&mut rng, 2048).expect("Failed to generate wrong key");
123
124        let payload = Claims {
125            sub: "1234567890".into(),
126            name: "Test User".into(),
127            iat: 1516239022,
128        };
129
130        let jws = create_signed_jws(&wrong_private_key, payload);
131
132        let result = jwk.verify_signature(jws);
133        assert!(
134            result.is_err(),
135            "Invalid signature should fail verification"
136        );
137
138        match result {
139            Err(Error::InvalidSignature { .. }) => {}
140            _ => panic!("Expected InvalidSignature error"),
141        }
142    }
143
144    #[test]
145    fn test_verify_tampered_payload() {
146        let (private_key, jwk) = create_test_key_and_jwk();
147
148        let original_payload = Claims {
149            sub: "1234567890".into(),
150            name: "Test User".into(),
151            iat: 1516239022,
152        };
153
154        let jws = create_signed_jws(&private_key, original_payload);
155        let signature: Cow<[u8]> = jws.signature();
156
157        // Manually tamper with the JWS by modifying the payload part
158
159        let tampered_payload = Claims {
160            sub: "9999999999".into(),
161            name: "Hacker".into(),
162            iat: 1516239022,
163        };
164
165        let tampered_jws = create_signed_jws(&private_key, tampered_payload);
166
167        let tampered_token = tampered_jws.encoded_token();
168
169        let tampered_jws = Jws::new(tampered_token.as_ref(), signature.as_ref());
170
171        let result = jwk.verify_signature(tampered_jws);
172        assert!(result.is_err(), "Tampered payload should fail verification");
173
174        match result {
175            Err(Error::InvalidSignature { .. }) => {}
176            _ => panic!("Expected InvalidSignature error for tampered payload"),
177        }
178    }
179}