Skip to main content

nacos_sdk/api/plugin/
encryption.rs

1use crate::api::plugin::{ConfigFilter, ConfigReq, ConfigResp};
2
3/**
4 * For example:cipher-AES-dataId.
5 */
6pub const DEFAULT_CIPHER_PREFIX: &str = "cipher-";
7
8pub const DEFAULT_CIPHER_SPLIT: &str = "-";
9
10/// EncryptionPlugin for Config.
11#[async_trait::async_trait]
12pub trait EncryptionPlugin: Send + Sync {
13    /**
14     * Whether need to do cipher.
15     *
16     * e.g. data_id = "cipher-AES-dataId"
17     */
18    fn need_cipher(&self, data_id: &str) -> bool {
19        data_id.starts_with(DEFAULT_CIPHER_PREFIX)
20            && self
21                .parse_algorithm_name(data_id)
22                .eq(&self.algorithm_name())
23    }
24
25    /**
26     * Parse encryption algorithm name.
27     *
28     * @param data_id data_id
29     * @return algorithm name
30     */
31    fn parse_algorithm_name(&self, data_id: &str) -> String {
32        data_id
33            .split(DEFAULT_CIPHER_SPLIT)
34            .nth(1)
35            .expect("data_id should have algorithm name in the second segment")
36            .to_string()
37    }
38
39    /**
40     * Encrypt interface.
41     *
42     * @param secret_key secret key
43     * @param content   content unencrypted
44     * @return encrypt value
45     */
46    async fn encrypt(&self, secret_key: &str, content: &str) -> String;
47
48    /**
49     * Decrypt interface.
50     *
51     * @param secret_key secret key
52     * @param content   encrypted
53     * @return decrypt value
54     */
55    async fn decrypt(&self, secret_key: &str, content: &str) -> String;
56
57    /**
58     * Generate secret key. It only be known by you.
59     *
60     * @return Secret key
61     */
62    async fn generate_secret_key(&self) -> String;
63
64    /**
65     * Algorithm name. e.g. AES,AES128,AES256,DES,3DES,...
66     *
67     * @return name
68     */
69    fn algorithm_name(&self) -> String;
70
71    /**
72     * Encrypt secret Key. It will be transmitted in the network.
73     *
74     * @param secretKey secretKey
75     * @return encrypted secretKey
76     */
77    async fn encrypt_secret_key(&self, secret_key: &str) -> String;
78
79    /**
80     * Decrypt secret Key.
81     *
82     * @param secret_key secretKey
83     * @return decrypted secretKey
84     */
85    async fn decrypt_secret_key(&self, secret_key: &str) -> String;
86}
87
88/// ConfigEncryptionFilter handle with [`EncryptionPlugin`]
89pub struct ConfigEncryptionFilter {
90    encryption_plugins: Vec<Box<dyn EncryptionPlugin>>,
91}
92
93impl ConfigEncryptionFilter {
94    pub fn new(encryption_plugins: Vec<Box<dyn EncryptionPlugin>>) -> Self {
95        Self { encryption_plugins }
96    }
97}
98
99#[async_trait::async_trait]
100impl ConfigFilter for ConfigEncryptionFilter {
101    async fn filter(
102        &self,
103        config_req: Option<&mut ConfigReq>,
104        config_resp: Option<&mut ConfigResp>,
105    ) {
106        // Publish configuration, encrypt
107        if let Some(config_req) = config_req {
108            for plugin in &self.encryption_plugins {
109                if !plugin.need_cipher(&config_req.data_id) {
110                    continue;
111                }
112
113                let secret_key = plugin.generate_secret_key().await;
114                let encrypted_content = plugin.encrypt(&secret_key, &config_req.content).await;
115                let encrypted_secret_key = plugin.encrypt_secret_key(&secret_key).await;
116
117                // set encrypted data.
118                config_req.encrypted_data_key = encrypted_secret_key;
119                config_req.content = encrypted_content;
120                break;
121            }
122        }
123
124        // Get configuration, decrypt
125        if let Some(config_resp) = config_resp
126            && !config_resp.encrypted_data_key.is_empty()
127        {
128            for plugin in &self.encryption_plugins {
129                if !plugin.need_cipher(&config_resp.data_id) {
130                    continue;
131                }
132
133                // get encrypted data.
134                let encrypted_secret_key = &config_resp.encrypted_data_key;
135                let encrypted_content = &config_resp.content;
136
137                let decrypted_secret_key = plugin.decrypt_secret_key(encrypted_secret_key).await;
138                let decrypted_content = plugin
139                    .decrypt(&decrypted_secret_key, encrypted_content)
140                    .await;
141
142                // set decrypted data.
143                config_resp.content = decrypted_content;
144                break;
145            }
146        }
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use crate::api::plugin::config_filter::{ConfigReq, ConfigResp};
153    use crate::api::plugin::encryption::DEFAULT_CIPHER_PREFIX;
154    use crate::api::plugin::{ConfigEncryptionFilter, ConfigFilter, EncryptionPlugin};
155
156    struct TestEncryptionPlugin;
157
158    #[async_trait::async_trait]
159    impl EncryptionPlugin for TestEncryptionPlugin {
160        async fn encrypt(&self, secret_key: &str, content: &str) -> String {
161            secret_key.to_owned() + content
162        }
163
164        async fn decrypt(&self, secret_key: &str, content: &str) -> String {
165            content.replace(secret_key, "")
166        }
167
168        async fn generate_secret_key(&self) -> String {
169            "secret-key".to_string()
170        }
171
172        fn algorithm_name(&self) -> String {
173            "TEST".to_string()
174        }
175
176        async fn encrypt_secret_key(&self, secret_key: &str) -> String {
177            "crypt_".to_owned() + secret_key
178        }
179
180        async fn decrypt_secret_key(&self, secret_key: &str) -> String {
181            secret_key.replace("crypt_", "")
182        }
183    }
184
185    #[tokio::test]
186    async fn test_config_encryption_filters_empty() {
187        let config_encryption_filter = ConfigEncryptionFilter::new(vec![]);
188
189        let (data_id, group, namespace, content, encrypted_data_key) = (
190            "D".to_string(),
191            "G".to_string(),
192            "N".to_string(),
193            "C".to_string(),
194            "".to_string(),
195        );
196
197        let mut config_req = ConfigReq::new(
198            data_id.clone(),
199            group.clone(),
200            namespace.clone(),
201            content.clone(),
202            encrypted_data_key.clone(),
203        );
204        config_encryption_filter
205            .filter(Some(&mut config_req), None)
206            .await;
207
208        assert_eq!(config_req.content, encrypted_data_key + content.as_str());
209
210        let mut config_resp = ConfigResp::new(
211            config_req.data_id.clone(),
212            config_req.group.clone(),
213            config_req.namespace.clone(),
214            config_req.content.clone(),
215            config_req.encrypted_data_key.clone(),
216        );
217        config_encryption_filter
218            .filter(None, Some(&mut config_resp))
219            .await;
220
221        assert_eq!(config_resp.content, content);
222    }
223
224    #[tokio::test]
225    async fn test_config_encryption_filters() {
226        let config_encryption_filter =
227            ConfigEncryptionFilter::new(vec![Box::new(TestEncryptionPlugin {})]);
228
229        let (data_id, group, namespace, content, encrypted_data_key) = (
230            DEFAULT_CIPHER_PREFIX.to_owned() + "-TEST-D",
231            "G".to_string(),
232            "N".to_string(),
233            "C".to_string(),
234            "E".to_string(),
235        );
236
237        let mut config_req = ConfigReq::new(
238            data_id.clone(),
239            group.clone(),
240            namespace.clone(),
241            content.clone(),
242            encrypted_data_key.clone(),
243        );
244        config_encryption_filter
245            .filter(Some(&mut config_req), None)
246            .await;
247
248        let mut config_resp = ConfigResp::new(
249            config_req.data_id.clone(),
250            config_req.group.clone(),
251            config_req.namespace.clone(),
252            config_req.content.clone(),
253            config_req.encrypted_data_key.clone(),
254        );
255        config_encryption_filter
256            .filter(None, Some(&mut config_resp))
257            .await;
258
259        assert_eq!(config_resp.content, content);
260    }
261
262    #[tokio::test]
263    async fn test_config_encryption_filters_not_need_cipher() {
264        let config_encryption_filter =
265            ConfigEncryptionFilter::new(vec![Box::new(TestEncryptionPlugin {})]);
266
267        let (data_id, group, namespace, content, encrypted_data_key) = (
268            "D".to_string(),
269            "G".to_string(),
270            "N".to_string(),
271            "C".to_string(),
272            "E".to_string(),
273        );
274
275        let mut config_req = ConfigReq::new(
276            data_id.clone(),
277            group.clone(),
278            namespace.clone(),
279            content.clone(),
280            encrypted_data_key.clone(),
281        );
282        config_encryption_filter
283            .filter(Some(&mut config_req), None)
284            .await;
285
286        let mut config_resp = ConfigResp::new(
287            config_req.data_id.clone(),
288            config_req.group.clone(),
289            config_req.namespace.clone(),
290            config_req.content.clone(),
291            config_req.encrypted_data_key.clone(),
292        );
293        config_encryption_filter
294            .filter(None, Some(&mut config_resp))
295            .await;
296
297        assert_eq!(config_resp.content, content);
298    }
299}