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