rocketmq_auth/authentication/chain/
acl_signer.rs1use base64::engine::general_purpose::STANDARD;
18use base64::Engine;
19use serde::Deserialize;
20use serde::Serialize;
21
22use crate::AuthFailureKind;
23use crate::AuthOperation;
24use crate::AuthServiceError;
25use crate::AuthServiceResult;
26
27#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
28pub enum SignatureAlgorithm {
29 #[default]
30 #[serde(
31 rename = "HmacSHA1",
32 alias = "hmacSHA1",
33 alias = "HMAC_SHA1",
34 alias = "HmacSha1",
35 alias = "sha1"
36 )]
37 HmacSha1,
38 #[serde(
39 rename = "HmacSHA256",
40 alias = "hmacSHA256",
41 alias = "HMAC_SHA256",
42 alias = "HmacSha256",
43 alias = "sha256"
44 )]
45 HmacSha256,
46 #[serde(
47 rename = "HmacMD5",
48 alias = "hmacMD5",
49 alias = "HMAC_MD5",
50 alias = "HmacMd5",
51 alias = "md5"
52 )]
53 HmacMd5,
54}
55
56impl SignatureAlgorithm {
57 pub const fn java_name(self) -> &'static str {
58 match self {
59 Self::HmacSha1 => "HmacSHA1",
60 Self::HmacSha256 => "HmacSHA256",
61 Self::HmacMd5 => "HmacMD5",
62 }
63 }
64
65 pub fn from_java_name(value: &str) -> Option<Self> {
66 match value.trim().to_ascii_uppercase().replace(['-', '_'], "").as_str() {
67 "HMACSHA1" | "SHA1" => Some(Self::HmacSha1),
68 "HMACSHA256" | "SHA256" => Some(Self::HmacSha256),
69 "HMACMD5" | "MD5" => Some(Self::HmacMd5),
70 _ => None,
71 }
72 }
73}
74
75pub fn cal_signature(content: &[u8], secret_key: &str) -> AuthServiceResult<String> {
86 cal_signature_with_algorithm(content, secret_key, SignatureAlgorithm::default())
87}
88
89pub fn cal_signature_segments<'a, I>(segments: I, secret_key: &str) -> AuthServiceResult<String>
90where
91 I: IntoIterator<Item = &'a [u8]>,
92{
93 cal_signature_segments_with_algorithm(segments, secret_key, SignatureAlgorithm::default())
94}
95
96pub fn cal_signature_with_algorithm(
97 content: &[u8],
98 secret_key: &str,
99 algorithm: SignatureAlgorithm,
100) -> AuthServiceResult<String> {
101 cal_signature_segments_with_algorithm([content], secret_key, algorithm)
102}
103
104pub fn cal_signature_segments_with_algorithm<'a, I>(
105 segments: I,
106 secret_key: &str,
107 algorithm: SignatureAlgorithm,
108) -> AuthServiceResult<String>
109where
110 I: IntoIterator<Item = &'a [u8]>,
111{
112 use hmac::digest::KeyInit;
113 use hmac::Hmac;
114 use hmac::Mac;
115
116 match algorithm {
117 SignatureAlgorithm::HmacSha1 => {
118 type HmacSha1 = Hmac<sha1::Sha1>;
119 let mut mac = HmacSha1::new_from_slice(secret_key.as_bytes()).map_err(|source| {
120 AuthServiceError::with_source(AuthOperation::Authenticate, AuthFailureKind::Internal, source)
121 })?;
122 for segment in segments {
123 mac.update(segment);
124 }
125 Ok(STANDARD.encode(mac.finalize().into_bytes().as_slice()))
126 }
127 SignatureAlgorithm::HmacSha256 => {
128 type HmacSha256 = Hmac<sha2::Sha256>;
129 let mut mac = HmacSha256::new_from_slice(secret_key.as_bytes()).map_err(|source| {
130 AuthServiceError::with_source(AuthOperation::Authenticate, AuthFailureKind::Internal, source)
131 })?;
132 for segment in segments {
133 mac.update(segment);
134 }
135 Ok(STANDARD.encode(mac.finalize().into_bytes().as_slice()))
136 }
137 SignatureAlgorithm::HmacMd5 => {
138 type HmacMd5 = Hmac<md5::Md5>;
139 let mut mac = HmacMd5::new_from_slice(secret_key.as_bytes()).map_err(|source| {
140 AuthServiceError::with_source(AuthOperation::Authenticate, AuthFailureKind::Internal, source)
141 })?;
142 for segment in segments {
143 mac.update(segment);
144 }
145 Ok(STANDARD.encode(mac.finalize().into_bytes().as_slice()))
146 }
147 }
148}
149
150pub const DEFAULT_CHARSET: &str = "UTF-8";
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_cal_signature() {
159 let content = b"test content";
160 let secret_key = "test_secret_key";
161
162 let result = cal_signature(content, secret_key);
163 assert!(result.is_ok());
164
165 let signature = result.unwrap();
166 assert!(!signature.is_empty());
167 }
168
169 #[test]
170 fn test_cal_signature_consistency() {
171 let content = b"consistent content";
172 let secret_key = "my_secret";
173
174 let sig1 = cal_signature(content, secret_key).unwrap();
175 let sig2 = cal_signature(content, secret_key).unwrap();
176
177 assert_eq!(sig1, sig2);
178 }
179
180 #[test]
181 fn test_cal_signature_different_keys() {
182 let content = b"same content";
183 let key1 = "key1";
184 let key2 = "key2";
185
186 let sig1 = cal_signature(content, key1).unwrap();
187 let sig2 = cal_signature(content, key2).unwrap();
188
189 assert_ne!(sig1, sig2);
190 }
191
192 #[test]
193 fn test_cal_signature_empty_content() {
194 let content = b"";
195 let secret_key = "secret";
196
197 let result = cal_signature(content, secret_key);
198 assert!(result.is_ok());
199 }
200
201 #[test]
202 fn test_cal_signature_matches_java_hmac_sha1_base64() {
203 let signature = cal_signature(b"alicetopic-a", "secret").unwrap();
204
205 assert_eq!(signature, "3yomro7y0WqWcbV+9tQa4av5w3Q=");
206 }
207
208 #[test]
209 fn test_cal_signature_segments_matches_contiguous_content() {
210 let contiguous = cal_signature(b"alicetokentopic-abody", "secret").unwrap();
211 let segmented = cal_signature_segments([b"alice".as_slice(), b"token", b"topic-a", b"body"], "secret").unwrap();
212
213 assert_eq!(segmented, contiguous);
214 }
215
216 #[test]
217 fn test_cal_signature_segments_supports_all_java_algorithms() {
218 for algorithm in [
219 SignatureAlgorithm::HmacSha1,
220 SignatureAlgorithm::HmacSha256,
221 SignatureAlgorithm::HmacMd5,
222 ] {
223 let contiguous = cal_signature_with_algorithm(b"alicetopic-a", "secret", algorithm).unwrap();
224 let segmented =
225 cal_signature_segments_with_algorithm([b"alice".as_slice(), b"topic-a"], "secret", algorithm).unwrap();
226
227 assert_eq!(segmented, contiguous);
228 }
229 }
230
231 #[test]
232 fn test_cal_signature_supports_java_signing_algorithm_enum() {
233 assert_eq!(
234 cal_signature_with_algorithm(b"alicetopic-a", "secret", SignatureAlgorithm::HmacSha1).unwrap(),
235 "3yomro7y0WqWcbV+9tQa4av5w3Q="
236 );
237 assert_eq!(
238 cal_signature_with_algorithm(b"alicetopic-a", "secret", SignatureAlgorithm::HmacSha256).unwrap(),
239 "+VoSl/q1CHGyZYyeI+H1C8SK6N+inDouZi6grscuA/Q="
240 );
241 assert_eq!(
242 cal_signature_with_algorithm(b"alicetopic-a", "secret", SignatureAlgorithm::HmacMd5).unwrap(),
243 "8NLZd12a3GBoWPkyeUsOJQ=="
244 );
245 }
246
247 #[test]
248 fn test_signature_algorithm_deserializes_java_names() {
249 assert_eq!(
250 serde_yaml::from_str::<SignatureAlgorithm>("HmacSHA1").unwrap(),
251 SignatureAlgorithm::HmacSha1
252 );
253 assert_eq!(
254 serde_yaml::from_str::<SignatureAlgorithm>("HmacSHA256").unwrap(),
255 SignatureAlgorithm::HmacSha256
256 );
257 assert_eq!(
258 serde_yaml::from_str::<SignatureAlgorithm>("HmacMD5").unwrap(),
259 SignatureAlgorithm::HmacMd5
260 );
261 }
262
263 #[test]
264 fn test_signature_algorithm_from_java_name_accepts_common_aliases() {
265 assert_eq!(
266 SignatureAlgorithm::from_java_name("HmacSHA1"),
267 Some(SignatureAlgorithm::HmacSha1)
268 );
269 assert_eq!(
270 SignatureAlgorithm::from_java_name("HMAC_SHA256"),
271 Some(SignatureAlgorithm::HmacSha256)
272 );
273 assert_eq!(
274 SignatureAlgorithm::from_java_name("md5"),
275 Some(SignatureAlgorithm::HmacMd5)
276 );
277 assert_eq!(SignatureAlgorithm::from_java_name("unknown"), None);
278 }
279}