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 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 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 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}