1use crate::{
8 ArcOutput, DkimOutput, DkimResult, Error, Version,
9 arc::Set,
10 common::{
11 crypto::{Algorithm, HashAlgorithm, SigningKey},
12 verify::VerifySignature,
13 },
14};
15
16pub mod builder;
17pub mod canonicalize;
18#[cfg(feature = "generate")]
19pub mod generate;
20pub mod headers;
21pub mod parse;
22pub mod sign;
23pub mod streaming;
24pub mod verify;
25
26pub use streaming::DkimSigningStream;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
29pub enum Canonicalization {
30 #[default]
31 Relaxed,
32 Simple,
33}
34
35#[derive(Debug, PartialEq, Eq, Clone, Default)]
36pub struct DkimSigner<T: SigningKey, State = NeedDomain> {
37 _state: std::marker::PhantomData<State>,
38 pub key: T,
39 pub template: Signature,
40}
41
42pub struct NeedDomain;
43pub struct NeedSelector;
44pub struct NeedHeaders;
45pub struct Done;
46
47#[derive(Debug, PartialEq, Eq, Clone, Default)]
48pub struct Signature {
49 pub v: u32,
50 pub a: Algorithm,
51 pub d: String,
52 pub s: String,
53 pub b: Vec<u8>,
54 pub bh: Vec<u8>,
55 pub h: Vec<String>,
56 pub z: Vec<String>,
57 pub i: String,
58 pub l: u64,
59 pub x: u64,
60 pub t: u64,
61 pub r: bool, pub atps: Option<String>, pub atpsh: Option<HashAlgorithm>, pub ch: Canonicalization,
65 pub cb: Canonicalization,
66}
67
68#[derive(Debug, PartialEq, Eq, Clone)]
69pub struct DomainKeyReport {
70 pub(crate) ra: String,
71 pub(crate) rp: u8,
72 pub(crate) rr: u8,
73 pub(crate) rs: Option<String>,
74}
75
76#[derive(Debug, PartialEq, Eq, Clone)]
77pub struct Atps {
78 pub(crate) v: Version,
79 pub(crate) d: Option<String>,
80}
81
82pub(crate) const R_SVC_ALL: u64 = 0x04;
83pub(crate) const R_SVC_EMAIL: u64 = 0x08;
84pub(crate) const R_FLAG_TESTING: u64 = 0x10;
85pub(crate) const R_FLAG_MATCH_DOMAIN: u64 = 0x20;
86
87pub(crate) const RR_DNS: u8 = 0x01;
88pub(crate) const RR_OTHER: u8 = 0x02;
89pub(crate) const RR_POLICY: u8 = 0x04;
90pub(crate) const RR_SIGNATURE: u8 = 0x08;
91pub(crate) const RR_UNKNOWN_TAG: u8 = 0x10;
92pub(crate) const RR_VERIFICATION: u8 = 0x20;
93pub(crate) const RR_EXPIRATION: u8 = 0x40;
94
95#[derive(Debug, PartialEq, Eq, Clone)]
96#[repr(u64)]
97pub(crate) enum Service {
98 All = R_SVC_ALL,
99 Email = R_SVC_EMAIL,
100}
101
102#[derive(Debug, PartialEq, Eq, Clone)]
103#[repr(u64)]
104pub(crate) enum Flag {
105 Testing = R_FLAG_TESTING,
106 MatchDomain = R_FLAG_MATCH_DOMAIN,
107}
108
109impl From<Flag> for u64 {
110 fn from(v: Flag) -> Self {
111 v as u64
112 }
113}
114
115impl From<HashAlgorithm> for u64 {
116 fn from(v: HashAlgorithm) -> Self {
117 v as u64
118 }
119}
120
121impl From<Service> for u64 {
122 fn from(v: Service) -> Self {
123 v as u64
124 }
125}
126
127impl From<Algorithm> for HashAlgorithm {
128 fn from(a: Algorithm) -> Self {
129 match a {
130 Algorithm::RsaSha256 | Algorithm::Ed25519Sha256 => HashAlgorithm::Sha256,
131 Algorithm::RsaSha1 => HashAlgorithm::Sha1,
132 }
133 }
134}
135
136impl VerifySignature for Signature {
137 fn signature(&self) -> &[u8] {
138 &self.b
139 }
140
141 fn algorithm(&self) -> Algorithm {
142 self.a
143 }
144
145 fn selector(&self) -> &str {
146 &self.s
147 }
148
149 fn domain(&self) -> &str {
150 &self.d
151 }
152}
153
154impl Signature {
155 pub fn identity(&self) -> &str {
156 &self.i
157 }
158}
159
160impl<'x> DkimOutput<'x> {
161 pub fn pass() -> Self {
162 DkimOutput {
163 result: DkimResult::Pass,
164 signature: None,
165 report: None,
166 is_atps: false,
167 }
168 }
169
170 pub fn perm_err(err: Error) -> Self {
171 DkimOutput {
172 result: DkimResult::PermError(err),
173 signature: None,
174 report: None,
175 is_atps: false,
176 }
177 }
178
179 pub fn temp_err(err: Error) -> Self {
180 DkimOutput {
181 result: DkimResult::TempError(err),
182 signature: None,
183 report: None,
184 is_atps: false,
185 }
186 }
187
188 pub fn fail(err: Error) -> Self {
189 DkimOutput {
190 result: DkimResult::Fail(err),
191 signature: None,
192 report: None,
193 is_atps: false,
194 }
195 }
196
197 pub fn neutral(err: Error) -> Self {
198 DkimOutput {
199 result: DkimResult::Neutral(err),
200 signature: None,
201 report: None,
202 is_atps: false,
203 }
204 }
205
206 pub fn dns_error(err: Error) -> Self {
207 if matches!(&err, Error::DnsError(_)) {
208 DkimOutput::temp_err(err)
209 } else {
210 DkimOutput::perm_err(err)
211 }
212 }
213
214 pub fn with_signature(mut self, signature: &'x Signature) -> Self {
215 self.signature = signature.into();
216 self
217 }
218
219 pub fn with_report(mut self, report: String) -> Self {
220 self.report = Some(report);
221 self
222 }
223
224 pub fn with_atps(mut self) -> Self {
225 self.is_atps = true;
226 self
227 }
228
229 pub fn result(&self) -> &DkimResult {
230 &self.result
231 }
232
233 pub fn signature(&self) -> Option<&Signature> {
234 self.signature
235 }
236
237 pub fn failure_report_addr(&self) -> Option<&str> {
238 self.report.as_deref()
239 }
240}
241
242impl ArcOutput<'_> {
243 pub fn result(&self) -> &DkimResult {
244 &self.result
245 }
246
247 pub fn sets(&'_ self) -> &'_ [Set<'_>] {
248 &self.set
249 }
250}
251
252impl From<Error> for DkimResult {
253 fn from(err: Error) -> Self {
254 if matches!(&err, Error::DnsError(_)) {
255 DkimResult::TempError(err)
256 } else {
257 DkimResult::PermError(err)
258 }
259 }
260}