1use mail_parser::decoders::base64::base64_decode_stream;
8
9use crate::{
10 common::{crypto::Algorithm, parse::TagParser},
11 dkim::{parse::SignatureParser, Canonicalization},
12 Error,
13};
14
15use super::{ChainValidation, Results, Seal, Signature};
16
17use crate::common::parse::*;
18
19pub(crate) const CV: u64 = (b'c' as u64) | ((b'v' as u64) << 8);
20
21impl Signature {
22 #[allow(clippy::while_let_on_iterator)]
23 pub fn parse(header: &'_ [u8]) -> crate::Result<Self> {
24 let mut signature = Signature {
25 a: Algorithm::RsaSha256,
26 d: "".into(),
27 s: "".into(),
28 b: Vec::with_capacity(0),
29 bh: Vec::with_capacity(0),
30 h: Vec::with_capacity(0),
31 z: Vec::with_capacity(0),
32 l: 0,
33 x: 0,
34 t: 0,
35 i: 0,
36 ch: Canonicalization::Simple,
37 cb: Canonicalization::Simple,
38 };
39 let header_len = header.len();
40 let mut header = header.iter();
41
42 while let Some(key) = header.key() {
43 match key {
44 I => {
45 signature.i = header.number().unwrap_or(0) as u32;
46 if !(1..=50).contains(&signature.i) {
47 return Err(Error::ArcInvalidInstance(signature.i));
48 }
49 }
50 A => {
51 signature.a = header.algorithm()?;
52 }
53 B => {
54 signature.b =
55 base64_decode_stream(&mut header, header_len, b';').ok_or(Error::Base64)?
56 }
57 BH => {
58 signature.bh =
59 base64_decode_stream(&mut header, header_len, b';').ok_or(Error::Base64)?
60 }
61 C => {
62 let (ch, cb) = header.canonicalization(Canonicalization::Simple)?;
63 signature.ch = ch;
64 signature.cb = cb;
65 }
66 D => signature.d = header.text(true),
67 H => signature.h = header.items(),
68 L => signature.l = header.number().unwrap_or(0),
69 S => signature.s = header.text(true),
70 T => signature.t = header.number().unwrap_or(0),
71 X => signature.x = header.number().unwrap_or(0),
72 Z => signature.z = header.headers_qp(),
73 _ => header.ignore(),
74 }
75 }
76
77 if !signature.d.is_empty()
78 && !signature.s.is_empty()
79 && !signature.b.is_empty()
80 && !signature.bh.is_empty()
81 && !signature.h.is_empty()
82 {
83 Ok(signature)
84 } else {
85 Err(Error::MissingParameters)
86 }
87 }
88}
89
90impl Seal {
91 #[allow(clippy::while_let_on_iterator)]
92 pub fn parse(header: &'_ [u8]) -> crate::Result<Self> {
93 let mut seal = Seal {
94 a: Algorithm::RsaSha256,
95 d: "".into(),
96 s: "".into(),
97 b: Vec::with_capacity(0),
98 t: 0,
99 i: 0,
100 cv: ChainValidation::None,
101 };
102 let header_len = header.len();
103 let mut header = header.iter();
104 let mut cv = None;
105
106 while let Some(key) = header.key() {
107 match key {
108 I => {
109 seal.i = header.number().unwrap_or(0) as u32;
110 }
111 A => {
112 seal.a = header.algorithm()?;
113 }
114 B => {
115 seal.b =
116 base64_decode_stream(&mut header, header_len, b';').ok_or(Error::Base64)?
117 }
118 D => seal.d = header.text(true),
119 S => seal.s = header.text(true),
120 T => seal.t = header.number().unwrap_or(0),
121 CV => {
122 match header.next_skip_whitespaces().unwrap_or(0) {
123 b'n' | b'N' if header.match_bytes(b"one") => {
124 cv = ChainValidation::None.into();
125 }
126 b'f' | b'F' if header.match_bytes(b"ail") => {
127 cv = ChainValidation::Fail.into();
128 }
129 b'p' | b'P' if header.match_bytes(b"ass") => {
130 cv = ChainValidation::Pass.into();
131 }
132 _ => return Err(Error::ArcInvalidCV),
133 }
134 if !header.seek_tag_end() {
135 return Err(Error::ArcInvalidCV);
136 }
137 }
138 H => {
139 return Err(Error::ArcHasHeaderTag);
140 }
141 _ => header.ignore(),
142 }
143 }
144 seal.cv = cv.ok_or(Error::ArcInvalidCV)?;
145
146 if !(1..=50).contains(&seal.i) {
147 Err(Error::ArcInvalidInstance(seal.i))
148 } else if !seal.d.is_empty() && !seal.s.is_empty() && !seal.b.is_empty() {
149 Ok(seal)
150 } else {
151 Err(Error::MissingParameters)
152 }
153 }
154}
155
156impl Results {
157 #[allow(clippy::while_let_on_iterator)]
158 pub fn parse(header: &'_ [u8]) -> crate::Result<Self> {
159 let mut results = Results { i: 0 };
160 let mut header = header.iter();
161
162 while let Some(key) = header.key() {
163 match key {
164 I => {
165 results.i = header.number().unwrap_or(0) as u32;
166 break;
167 }
168 _ => header.ignore(),
169 }
170 }
171
172 if (1..=50).contains(&results.i) {
173 Ok(results)
174 } else {
175 Err(Error::ArcInvalidInstance(results.i))
176 }
177 }
178}