asjeeves_encryption/
signature_verification.rs1use 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 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 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 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}