easyofd_core/signatures/
signed_info.rs1use super::references::References;
9use super::seal::Seal;
10use super::stamp_annot::StampAnnot;
11
12#[derive(Debug, Clone)]
14pub struct Provider {
15 pub name: String,
17 pub version: Option<String>,
19 pub company: Option<String>,
21}
22
23impl Provider {
24 #[must_use]
26 pub fn new(name: impl Into<String>) -> Self {
27 Self {
28 name: name.into(),
29 version: None,
30 company: None,
31 }
32 }
33
34 #[must_use]
36 pub fn version(mut self, version: impl Into<String>) -> Self {
37 self.version = Some(version.into());
38 self
39 }
40
41 #[must_use]
43 pub fn company(mut self, company: impl Into<String>) -> Self {
44 self.company = Some(company.into());
45 self
46 }
47
48 #[must_use]
50 pub fn to_xml_string(&self) -> String {
51 let mut xml = format!(r#"<ofd:Provider Name="{}""#, self.name);
52 if let Some(ref v) = self.version {
53 use std::fmt::Write;
54 let _ = write!(xml, r#" Version="{v}""#);
55 }
56 if let Some(ref c) = self.company {
57 use std::fmt::Write;
58 let _ = write!(xml, r#" Company="{c}""#);
59 }
60 xml.push_str(" />");
61 xml
62 }
63}
64
65#[derive(Debug, Clone)]
70pub struct SignedInfo {
71 pub provider: Provider,
73 pub signature_method: Option<String>,
75 pub signature_datetime: Option<String>,
77 pub references: References,
79 pub stamp_annots: Vec<StampAnnot>,
81 pub seal: Option<Seal>,
83}
84
85impl SignedInfo {
86 #[must_use]
88 pub fn new(provider: Provider, references: References) -> Self {
89 Self {
90 provider,
91 signature_method: None,
92 signature_datetime: None,
93 references,
94 stamp_annots: Vec::new(),
95 seal: None,
96 }
97 }
98
99 #[must_use]
101 pub fn signature_method(mut self, method: impl Into<String>) -> Self {
102 self.signature_method = Some(method.into());
103 self
104 }
105
106 #[must_use]
108 pub fn signature_datetime(mut self, datetime: impl Into<String>) -> Self {
109 self.signature_datetime = Some(datetime.into());
110 self
111 }
112
113 #[must_use]
115 pub fn add_stamp_annot(mut self, stamp_annot: StampAnnot) -> Self {
116 self.stamp_annots.push(stamp_annot);
117 self
118 }
119
120 #[must_use]
122 pub fn seal(mut self, seal: Seal) -> Self {
123 self.seal = Some(seal);
124 self
125 }
126
127 #[must_use]
129 pub fn to_xml_string(&self) -> String {
130 use std::fmt::Write;
131
132 let mut xml = String::from("<ofd:SignedInfo>");
133 xml.push('\n');
134 xml.push_str(&self.provider.to_xml_string());
135
136 if let Some(ref method) = self.signature_method {
137 xml.push('\n');
138 let _ = write!(xml, "<ofd:SignatureMethod>{method}</ofd:SignatureMethod>");
139 }
140 if let Some(ref dt) = self.signature_datetime {
141 xml.push('\n');
142 let _ = write!(xml, "<ofd:SignatureDateTime>{dt}</ofd:SignatureDateTime>");
143 }
144
145 xml.push('\n');
146 xml.push_str(&self.references.to_xml_string());
147
148 for sa in &self.stamp_annots {
149 xml.push('\n');
150 xml.push_str(&sa.to_xml_string());
151 }
152
153 if let Some(ref seal) = self.seal {
154 xml.push('\n');
155 xml.push_str(&seal.to_xml_string());
156 }
157
158 xml.push_str("\n</ofd:SignedInfo>");
159 xml
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use crate::basic_type::{ST_Box, ST_RefID};
167
168 #[test]
169 fn test_signed_info_new() {
170 let si = SignedInfo::new(
171 Provider::new("TestProvider"),
172 References::new().add_reference(crate::signatures::reference::Reference::new(
173 "/Doc_0/Document.xml",
174 "hash",
175 )),
176 );
177 assert_eq!(si.provider.name, "TestProvider");
178 assert!(si.signature_method.is_none());
179 assert!(si.seal.is_none());
180 assert!(si.stamp_annots.is_empty());
181 }
182
183 #[test]
184 fn test_signed_info_builder() {
185 let si = SignedInfo::new(
186 Provider::new("MyProvider").version("1.0").company("MyCo"),
187 References::new().check_method("SM3"),
188 )
189 .signature_method("1.2.156.10197.1.501")
190 .signature_datetime("2025-01-01T00:00:00")
191 .add_stamp_annot(StampAnnot::new(
192 "s1",
193 ST_RefID::new(1),
194 ST_Box::new(0.0, 0.0, 100.0, 100.0),
195 ))
196 .seal(Seal::new("/Doc_0/Signs/Sign_0/Seal.esl"));
197 assert_eq!(si.signature_method.as_deref(), Some("1.2.156.10197.1.501"));
198 assert_eq!(
199 si.signature_datetime.as_deref(),
200 Some("2025-01-01T00:00:00")
201 );
202 assert_eq!(si.stamp_annots.len(), 1);
203 assert!(si.seal.is_some());
204 }
205
206 #[test]
207 fn test_signed_info_xml() {
208 let si = SignedInfo::new(
209 Provider::new("Prov"),
210 References::new().add_reference(crate::signatures::reference::Reference::new(
211 "/Doc_0/Document.xml",
212 "abc",
213 )),
214 )
215 .signature_method("SM2");
216 let xml = si.to_xml_string();
217 assert!(xml.contains("<ofd:SignedInfo>"));
218 assert!(xml.contains("Name=\"Prov\""));
219 assert!(xml.contains("<ofd:SignatureMethod>SM2</ofd:SignatureMethod>"));
220 assert!(xml.contains("</ofd:SignedInfo>"));
221 }
222
223 #[test]
224 fn test_provider_builder() {
225 let p = Provider::new("Test").version("2.0").company("ACME");
226 assert_eq!(p.name, "Test");
227 assert_eq!(p.version.as_deref(), Some("2.0"));
228 assert_eq!(p.company.as_deref(), Some("ACME"));
229 let xml = p.to_xml_string();
230 assert!(xml.contains("Name=\"Test\""));
231 assert!(xml.contains("Version=\"2.0\""));
232 assert!(xml.contains("Company=\"ACME\""));
233 }
234}