modo/auth/session/jwt/
signer.rs1use std::sync::Arc;
2
3use hmac::{Hmac, KeyInit, Mac};
4use sha2::Sha256;
5
6use crate::{Error, Result};
7
8use super::error::JwtError;
9
10type HmacSha256 = Hmac<Sha256>;
11
12pub trait TokenVerifier: Send + Sync {
17 fn verify(&self, header_payload: &[u8], signature: &[u8]) -> Result<()>;
25 fn algorithm_name(&self) -> &str;
27}
28
29pub trait TokenSigner: TokenVerifier {
34 fn sign(&self, header_payload: &[u8]) -> Result<Vec<u8>>;
41}
42
43pub struct HmacSigner {
47 inner: Arc<HmacSignerInner>,
48}
49
50struct HmacSignerInner {
51 secret: Vec<u8>,
52}
53
54impl HmacSigner {
55 pub fn new(secret: impl AsRef<[u8]>) -> Self {
57 Self {
58 inner: Arc::new(HmacSignerInner {
59 secret: secret.as_ref().to_vec(),
60 }),
61 }
62 }
63}
64
65impl Clone for HmacSigner {
66 fn clone(&self) -> Self {
67 Self {
68 inner: self.inner.clone(),
69 }
70 }
71}
72
73impl TokenVerifier for HmacSigner {
74 fn verify(&self, header_payload: &[u8], signature: &[u8]) -> Result<()> {
75 let mut mac = HmacSha256::new_from_slice(&self.inner.secret)
76 .map_err(|_| Error::internal("invalid HMAC key").chain(JwtError::InvalidSignature))?;
77 mac.update(header_payload);
78 mac.verify_slice(signature).map_err(|_| {
79 Error::unauthorized("unauthorized")
80 .chain(JwtError::InvalidSignature)
81 .with_code(JwtError::InvalidSignature.code())
82 })
83 }
84
85 fn algorithm_name(&self) -> &str {
86 "HS256"
87 }
88}
89
90impl TokenSigner for HmacSigner {
91 fn sign(&self, header_payload: &[u8]) -> Result<Vec<u8>> {
92 let mut mac = HmacSha256::new_from_slice(&self.inner.secret)
93 .map_err(|_| Error::internal("invalid HMAC key").chain(JwtError::SigningFailed))?;
94 mac.update(header_payload);
95 Ok(mac.finalize().into_bytes().to_vec())
96 }
97}
98
99impl From<HmacSigner> for Arc<dyn TokenSigner> {
100 fn from(signer: HmacSigner) -> Self {
101 Arc::new(signer)
102 }
103}
104
105impl From<HmacSigner> for Arc<dyn TokenVerifier> {
106 fn from(signer: HmacSigner) -> Self {
107 Arc::new(signer)
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn sign_verify_roundtrip() {
117 let signer = HmacSigner::new(b"secret-key");
118 let data = b"header.payload";
119 let sig = signer.sign(data).unwrap();
120 assert!(signer.verify(data, &sig).is_ok());
121 }
122
123 #[test]
124 fn verify_rejects_tampered_payload() {
125 let signer = HmacSigner::new(b"secret-key");
126 let sig = signer.sign(b"header.payload").unwrap();
127 let result = signer.verify(b"header.tampered", &sig);
128 assert!(result.is_err());
129 let err = result.unwrap_err();
130 assert_eq!(err.status(), http::StatusCode::UNAUTHORIZED);
131 }
132
133 #[test]
134 fn verify_rejects_wrong_secret() {
135 let signer1 = HmacSigner::new(b"secret-one");
136 let signer2 = HmacSigner::new(b"secret-two");
137 let sig = signer1.sign(b"data").unwrap();
138 assert!(signer2.verify(b"data", &sig).is_err());
139 }
140
141 #[test]
142 fn algorithm_name_returns_hs256() {
143 let signer = HmacSigner::new(b"key");
144 assert_eq!(signer.algorithm_name(), "HS256");
145 }
146
147 #[test]
148 fn clone_shares_inner() {
149 let signer = HmacSigner::new(b"key");
150 let cloned = signer.clone();
151 let sig = signer.sign(b"data").unwrap();
152 assert!(cloned.verify(b"data", &sig).is_ok());
153 }
154
155 #[test]
156 fn into_arc_dyn_token_signer() {
157 let signer = HmacSigner::new(b"key");
158 let arc_signer: Arc<dyn TokenSigner> = signer.into();
159 assert_eq!(arc_signer.algorithm_name(), "HS256");
160 }
161
162 #[test]
163 fn into_arc_dyn_token_verifier() {
164 let signer = HmacSigner::new(b"key");
165 let arc_verifier: Arc<dyn TokenVerifier> = signer.into();
166 assert_eq!(arc_verifier.algorithm_name(), "HS256");
167 }
168}