Skip to main content

easyofd_core/crypto/
provider.rs

1//! 加密组件提供者(Provider)。
2//!
3//! 对应 Java: org.ofdrw.core.crypto.encryt.Provider
4//!
5//! GMT0099 C.3 表 C.2,描述加密组件的相关信息。
6//! 与签名组件 Provider(`signatures::Provider`)的区别在于:
7//! 加密 Provider 使用 `Name` 属性,签名 Provider 使用 `ProviderName` 属性。
8
9/// 加密组件提供者。
10///
11/// 对应 Java: `org.ofdrw.core.crypto.encryt.Provider`
12///
13/// 描述加密组件的相关信息(GMT0099 C.3 表 C.2)。
14/// 与签名组件 [`signatures::Provider`](crate::signatures::Provider) 不同,
15/// 此类型使用 `Name` 属性而非 `ProviderName`。
16#[derive(Debug, Clone)]
17pub struct CryptoProvider {
18    /// 加密组件名称(必选)。
19    name: String,
20    /// 加密组件版本(可选)。
21    version: Option<String>,
22    /// 加密组件制造商(可选)。
23    company: Option<String>,
24    /// 接口协议版本(可选,OFD 2.0)。
25    protocol_ver: Option<String>,
26    /// 扩展信息(可选,OFD 2.0),Base64 编码前的原始字节。
27    extend_data: Option<Vec<u8>>,
28}
29
30impl CryptoProvider {
31    /// 创建加密组件提供者。
32    ///
33    /// # 参数
34    ///
35    /// - `name`:加密组件名称(必选)。
36    ///
37    /// # 对应 Java
38    ///
39    /// `org.ofdrw.core.crypto.encryt.Provider#setProviderName(String)`
40    #[must_use]
41    pub fn new(name: impl Into<String>) -> Self {
42        Self {
43            name: name.into(),
44            version: None,
45            company: None,
46            protocol_ver: None,
47            extend_data: None,
48        }
49    }
50
51    /// 获取加密组件名称。
52    ///
53    /// 对应 Java: `getProviderName()` → 读取 `Name` 属性
54    #[must_use]
55    pub fn name(&self) -> &str {
56        &self.name
57    }
58
59    /// 设置加密组件版本。
60    ///
61    /// 对应 Java: `setVersion(String)`
62    #[must_use]
63    pub fn version(mut self, version: impl Into<String>) -> Self {
64        self.version = Some(version.into());
65        self
66    }
67
68    /// 获取加密组件版本。
69    #[must_use]
70    pub fn version_ref(&self) -> Option<&str> {
71        self.version.as_deref()
72    }
73
74    /// 设置加密组件制造商。
75    ///
76    /// 对应 Java: `setCompany(String)`
77    #[must_use]
78    pub fn company(mut self, company: impl Into<String>) -> Self {
79        self.company = Some(company.into());
80        self
81    }
82
83    /// 获取加密组件制造商。
84    #[must_use]
85    pub fn company_ref(&self) -> Option<&str> {
86        self.company.as_deref()
87    }
88
89    /// 设置接口协议版本(OFD 2.0)。
90    ///
91    /// 对应 Java: `setProtocolVer(String)`
92    #[must_use]
93    pub fn protocol_ver(mut self, ver: impl Into<String>) -> Self {
94        self.protocol_ver = Some(ver.into());
95        self
96    }
97
98    /// 获取接口协议版本。
99    #[must_use]
100    pub fn protocol_ver_ref(&self) -> Option<&str> {
101        self.protocol_ver.as_deref()
102    }
103
104    /// 设置扩展信息(OFD 2.0)。
105    ///
106    /// 原始字节在序列化时会被 Base64 编码。
107    ///
108    /// 对应 Java: `setExtendData(byte[])`
109    #[must_use]
110    pub fn extend_data(mut self, data: Vec<u8>) -> Self {
111        self.extend_data = Some(data);
112        self
113    }
114
115    /// 获取扩展信息原始字节。
116    #[must_use]
117    pub fn extend_data_ref(&self) -> Option<&[u8]> {
118        self.extend_data.as_deref()
119    }
120
121    /// 序列化为 XML 字符串。
122    ///
123    /// 产出格式与 Java `Provider.toString()` 一致:
124    /// `<ofd:Provider Name="..." Version="..." Company="..." ProtocolVer="...">`
125    #[must_use]
126    pub fn to_xml_string(&self) -> String {
127        use std::fmt::Write;
128
129        let mut xml = format!(r#"<ofd:Provider Name="{}""#, self.name);
130        if let Some(ref v) = self.version {
131            let _ = write!(xml, r#" Version="{v}""#);
132        }
133        if let Some(ref c) = self.company {
134            let _ = write!(xml, r#" Company="{c}""#);
135        }
136        if let Some(ref p) = self.protocol_ver {
137            let _ = write!(xml, r#" ProtocolVer="{p}""#);
138        }
139        if let Some(ref data) = self.extend_data {
140            let encoded = base64_encode(data);
141            let _ = write!(
142                xml,
143                "><ofd:ExtendData>{encoded}</ofd:ExtendData></ofd:Provider>"
144            );
145        } else {
146            xml.push_str(" />");
147        }
148        xml
149    }
150}
151
152/// 简单的 Base64 编码实现(避免引入额外依赖)。
153fn base64_encode(data: &[u8]) -> String {
154    const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
155    let mut out = String::with_capacity(data.len().div_ceil(3) * 4);
156    for chunk in data.chunks(3) {
157        let b0 = u32::from(chunk[0]);
158        let b1 = if chunk.len() > 1 {
159            u32::from(chunk[1])
160        } else {
161            0
162        };
163        let b2 = if chunk.len() > 2 {
164            u32::from(chunk[2])
165        } else {
166            0
167        };
168        let triple = (b0 << 16) | (b1 << 8) | b2;
169        out.push(CHARS[((triple >> 18) & 0x3F) as usize] as char);
170        out.push(CHARS[((triple >> 12) & 0x3F) as usize] as char);
171        if chunk.len() > 1 {
172            out.push(CHARS[((triple >> 6) & 0x3F) as usize] as char);
173        } else {
174            out.push('=');
175        }
176        if chunk.len() > 2 {
177            out.push(CHARS[(triple & 0x3F) as usize] as char);
178        } else {
179            out.push('=');
180        }
181    }
182    out
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn new_provider() {
191        let p = CryptoProvider::new("TestCrypto");
192        assert_eq!(p.name(), "TestCrypto");
193        assert!(p.version_ref().is_none());
194        assert!(p.company_ref().is_none());
195        assert!(p.protocol_ver_ref().is_none());
196        assert!(p.extend_data_ref().is_none());
197    }
198
199    #[test]
200    fn builder_chain() {
201        let p = CryptoProvider::new("SM4Provider")
202            .version("1.0")
203            .company("ACME")
204            .protocol_ver("2.0");
205        assert_eq!(p.name(), "SM4Provider");
206        assert_eq!(p.version_ref(), Some("1.0"));
207        assert_eq!(p.company_ref(), Some("ACME"));
208        assert_eq!(p.protocol_ver_ref(), Some("2.0"));
209    }
210
211    #[test]
212    fn xml_name_only() {
213        let p = CryptoProvider::new("TestProvider");
214        let xml = p.to_xml_string();
215        assert!(xml.contains(r#"Name="TestProvider""#));
216        assert!(xml.contains("<ofd:Provider"));
217        assert!(xml.ends_with("/>"));
218    }
219
220    #[test]
221    fn xml_all_attributes() {
222        let p = CryptoProvider::new("SM4")
223            .version("1.0")
224            .company("TestCo")
225            .protocol_ver("2.0");
226        let xml = p.to_xml_string();
227        assert!(xml.contains(r#"Name="SM4""#));
228        assert!(xml.contains(r#"Version="1.0""#));
229        assert!(xml.contains(r#"Company="TestCo""#));
230        assert!(xml.contains(r#"ProtocolVer="2.0""#));
231    }
232
233    #[test]
234    fn xml_with_extend_data() {
235        let p = CryptoProvider::new("Test").extend_data(vec![0x01, 0x02, 0x03]);
236        let xml = p.to_xml_string();
237        assert!(xml.contains("<ofd:ExtendData>"));
238        assert!(xml.contains("</ofd:Provider>"));
239        // Base64 of [1,2,3] = "AQID"
240        assert!(xml.contains("AQID"));
241    }
242
243    #[test]
244    fn base64_encode_basic() {
245        assert_eq!(base64_encode(b""), "");
246        assert_eq!(base64_encode(b"f"), "Zg==");
247        assert_eq!(base64_encode(b"fo"), "Zm8=");
248        assert_eq!(base64_encode(b"foo"), "Zm9v");
249        assert_eq!(base64_encode(b"foob"), "Zm9vYg==");
250        assert_eq!(base64_encode(b"fooba"), "Zm9vYmE=");
251        assert_eq!(base64_encode(b"foobar"), "Zm9vYmFy");
252    }
253
254    #[test]
255    fn clone_provider() {
256        let p = CryptoProvider::new("Test").version("1.0");
257        let cloned = p.clone();
258        assert_eq!(cloned.name(), "Test");
259        assert_eq!(cloned.version_ref(), Some("1.0"));
260    }
261
262    #[test]
263    fn extend_data_ref() {
264        let p = CryptoProvider::new("Test").extend_data(vec![10, 20, 30]);
265        assert_eq!(p.extend_data_ref(), Some(&[10u8, 20, 30][..]));
266    }
267}