Commit
+219 -197 +/-1 browse
1 | diff --git a/src/common/crypto.rs b/src/common/crypto.rs |
2 | index b5e353d..76e20bb 100644 |
3 | --- a/src/common/crypto.rs |
4 | +++ b/src/common/crypto.rs |
5 | @@ -1,9 +1,4 @@ |
6 | - use std::marker::PhantomData; |
7 | - |
8 | - use ed25519_dalek::Signer; |
9 | - use rsa::{pkcs1::DecodeRsaPrivateKey, PaddingScheme, PublicKey as _, RsaPrivateKey}; |
10 | - use sha1::digest::Output; |
11 | - use sha2::digest::Digest; |
12 | + use sha1::{digest::Output, Digest}; |
13 | |
14 | use crate::{dkim::Canonicalization, Error, Result}; |
15 | |
16 | @@ -21,100 +16,251 @@ pub trait SigningKey { |
17 | fn algorithm(&self) -> Algorithm; |
18 | } |
19 | |
20 | - #[derive(Debug, Clone)] |
21 | - pub struct RsaKey<T> { |
22 | - inner: RsaPrivateKey, |
23 | - padding: PhantomData<T>, |
24 | - } |
25 | + mod rust_crypto { |
26 | + use std::marker::PhantomData; |
27 | + |
28 | + use ed25519_dalek::Signer; |
29 | + use rsa::{pkcs1::DecodeRsaPrivateKey, PaddingScheme, PublicKey as _, RsaPrivateKey}; |
30 | + use sha2::digest::Digest; |
31 | + |
32 | + use crate::{common::headers::Writer, dkim::Canonicalization}; |
33 | |
34 | - impl<T: HashImpl> RsaKey<T> { |
35 | - /// Creates a new RSA private key from a PKCS1 PEM string. |
36 | - pub fn from_pkcs1_pem(private_key_pem: &str) -> Result<Self> { |
37 | - let inner = RsaPrivateKey::from_pkcs1_pem(private_key_pem) |
38 | - .map_err(|err| Error::CryptoError(err.to_string()))?; |
39 | + use super::{ |
40 | + Algorithm, Error, HashContext, HashImpl, HashOutput, Result, Sha1, Sha256, SigningKey, |
41 | + VerifyingKey, |
42 | + }; |
43 | |
44 | - Ok(RsaKey { |
45 | - inner, |
46 | - padding: PhantomData, |
47 | - }) |
48 | + #[derive(Clone, Debug)] |
49 | + pub struct RsaKey<T> { |
50 | + inner: RsaPrivateKey, |
51 | + padding: PhantomData<T>, |
52 | } |
53 | |
54 | - /// Creates a new RSA private key from a PKCS1 binary slice. |
55 | - pub fn from_pkcs1_der(private_key_bytes: &[u8]) -> Result<Self> { |
56 | - let inner = RsaPrivateKey::from_pkcs1_der(private_key_bytes) |
57 | - .map_err(|err| Error::CryptoError(err.to_string()))?; |
58 | + impl<T: HashImpl> RsaKey<T> { |
59 | + /// Creates a new RSA private key from a PKCS1 PEM string. |
60 | + pub fn from_pkcs1_pem(private_key_pem: &str) -> Result<Self> { |
61 | + let inner = RsaPrivateKey::from_pkcs1_pem(private_key_pem) |
62 | + .map_err(|err| Error::CryptoError(err.to_string()))?; |
63 | |
64 | - Ok(RsaKey { |
65 | - inner, |
66 | - padding: PhantomData, |
67 | - }) |
68 | + Ok(RsaKey { |
69 | + inner, |
70 | + padding: PhantomData, |
71 | + }) |
72 | + } |
73 | + |
74 | + /// Creates a new RSA private key from a PKCS1 binary slice. |
75 | + pub fn from_pkcs1_der(private_key_bytes: &[u8]) -> Result<Self> { |
76 | + let inner = RsaPrivateKey::from_pkcs1_der(private_key_bytes) |
77 | + .map_err(|err| Error::CryptoError(err.to_string()))?; |
78 | + |
79 | + Ok(RsaKey { |
80 | + inner, |
81 | + padding: PhantomData, |
82 | + }) |
83 | + } |
84 | } |
85 | - } |
86 | |
87 | - impl SigningKey for RsaKey<Sha1> { |
88 | - type Hasher = Sha1; |
89 | + impl SigningKey for RsaKey<Sha1> { |
90 | + type Hasher = Sha1; |
91 | + |
92 | + fn sign(&self, data: HashOutput) -> Result<Vec<u8>> { |
93 | + self.inner |
94 | + .sign( |
95 | + PaddingScheme::new_pkcs1v15_sign::<<Self::Hasher as HashImpl>::Context>(), |
96 | + data.as_ref(), |
97 | + ) |
98 | + .map_err(|err| Error::CryptoError(err.to_string())) |
99 | + } |
100 | |
101 | - fn sign(&self, data: HashOutput) -> Result<Vec<u8>> { |
102 | - self.inner |
103 | - .sign( |
104 | - PaddingScheme::new_pkcs1v15_sign::<<Self::Hasher as HashImpl>::Context>(), |
105 | - data.as_ref(), |
106 | - ) |
107 | - .map_err(|err| Error::CryptoError(err.to_string())) |
108 | + fn algorithm(&self) -> Algorithm { |
109 | + Algorithm::RsaSha1 |
110 | + } |
111 | } |
112 | |
113 | - fn algorithm(&self) -> Algorithm { |
114 | - Algorithm::RsaSha1 |
115 | + impl SigningKey for RsaKey<Sha256> { |
116 | + type Hasher = Sha256; |
117 | + |
118 | + fn sign(&self, data: HashOutput) -> Result<Vec<u8>> { |
119 | + self.inner |
120 | + .sign( |
121 | + PaddingScheme::new_pkcs1v15_sign::<<Self::Hasher as HashImpl>::Context>(), |
122 | + data.as_ref(), |
123 | + ) |
124 | + .map_err(|err| Error::CryptoError(err.to_string())) |
125 | + } |
126 | + |
127 | + fn algorithm(&self) -> Algorithm { |
128 | + Algorithm::RsaSha256 |
129 | + } |
130 | } |
131 | - } |
132 | |
133 | - impl SigningKey for RsaKey<Sha256> { |
134 | - type Hasher = Sha256; |
135 | + pub struct Ed25519Key { |
136 | + inner: ed25519_dalek::Keypair, |
137 | + } |
138 | |
139 | - fn sign(&self, data: HashOutput) -> Result<Vec<u8>> { |
140 | - self.inner |
141 | - .sign( |
142 | - PaddingScheme::new_pkcs1v15_sign::<<Self::Hasher as HashImpl>::Context>(), |
143 | - data.as_ref(), |
144 | - ) |
145 | - .map_err(|err| Error::CryptoError(err.to_string())) |
146 | + impl Ed25519Key { |
147 | + /// Creates an Ed25519 private key |
148 | + pub fn from_bytes( |
149 | + public_key_bytes: &[u8], |
150 | + private_key_bytes: &[u8], |
151 | + ) -> crate::Result<Self> { |
152 | + Ok(Self { |
153 | + inner: ed25519_dalek::Keypair { |
154 | + public: ed25519_dalek::PublicKey::from_bytes(public_key_bytes) |
155 | + .map_err(|err| Error::CryptoError(err.to_string()))?, |
156 | + secret: ed25519_dalek::SecretKey::from_bytes(private_key_bytes) |
157 | + .map_err(|err| Error::CryptoError(err.to_string()))?, |
158 | + }, |
159 | + }) |
160 | + } |
161 | } |
162 | |
163 | - fn algorithm(&self) -> Algorithm { |
164 | - Algorithm::RsaSha256 |
165 | + impl SigningKey for Ed25519Key { |
166 | + type Hasher = Sha256; |
167 | + |
168 | + fn sign(&self, data: HashOutput) -> Result<Vec<u8>> { |
169 | + Ok(self.inner.sign(data.as_ref()).to_bytes().to_vec()) |
170 | + } |
171 | + |
172 | + fn algorithm(&self) -> Algorithm { |
173 | + Algorithm::Ed25519Sha256 |
174 | + } |
175 | } |
176 | - } |
177 | |
178 | - pub struct Ed25519Key { |
179 | - inner: ed25519_dalek::Keypair, |
180 | - } |
181 | + pub(crate) struct RsaPublicKey { |
182 | + inner: rsa::RsaPublicKey, |
183 | + } |
184 | |
185 | - impl Ed25519Key { |
186 | - /// Creates an Ed25519 private key |
187 | - pub fn from_bytes(public_key_bytes: &[u8], private_key_bytes: &[u8]) -> crate::Result<Self> { |
188 | - Ok(Self { |
189 | - inner: ed25519_dalek::Keypair { |
190 | - public: ed25519_dalek::PublicKey::from_bytes(public_key_bytes) |
191 | - .map_err(|err| Error::CryptoError(err.to_string()))?, |
192 | - secret: ed25519_dalek::SecretKey::from_bytes(private_key_bytes) |
193 | + impl RsaPublicKey { |
194 | + pub(crate) fn verifying_key_from_bytes( |
195 | + bytes: &[u8], |
196 | + ) -> Result<Box<dyn VerifyingKey + Send + Sync>> { |
197 | + Ok(Box::new(RsaPublicKey { |
198 | + inner: <rsa::RsaPublicKey as rsa::pkcs8::DecodePublicKey>::from_public_key_der( |
199 | + bytes, |
200 | + ) |
201 | + .or_else(|_| rsa::pkcs1::DecodeRsaPublicKey::from_pkcs1_der(bytes)) |
202 | + .map_err(|err| Error::CryptoError(err.to_string()))?, |
203 | + })) |
204 | + } |
205 | + } |
206 | + |
207 | + impl VerifyingKey for RsaPublicKey { |
208 | + fn verify<'a>( |
209 | + &self, |
210 | + headers: &mut dyn Iterator<Item = (&'a [u8], &'a [u8])>, |
211 | + signature: &[u8], |
212 | + canonicalization: Canonicalization, |
213 | + algorithm: Algorithm, |
214 | + ) -> Result<()> { |
215 | + match algorithm { |
216 | + Algorithm::RsaSha256 => { |
217 | + let hash = canonicalization.hash_headers::<Sha256>(headers); |
218 | + self.inner |
219 | + .verify( |
220 | + PaddingScheme::new_pkcs1v15_sign::<sha2::Sha256>(), |
221 | + hash.as_ref(), |
222 | + signature, |
223 | + ) |
224 | + .map_err(|_| Error::FailedVerification) |
225 | + } |
226 | + Algorithm::RsaSha1 => { |
227 | + let hash = canonicalization.hash_headers::<Sha1>(headers); |
228 | + self.inner |
229 | + .verify( |
230 | + PaddingScheme::new_pkcs1v15_sign::<sha1::Sha1>(), |
231 | + hash.as_ref(), |
232 | + signature, |
233 | + ) |
234 | + .map_err(|_| Error::FailedVerification) |
235 | + } |
236 | + Algorithm::Ed25519Sha256 => Err(Error::IncompatibleAlgorithms), |
237 | + } |
238 | + } |
239 | + } |
240 | + |
241 | + pub(crate) struct Ed25519PublicKey { |
242 | + inner: ed25519_dalek::PublicKey, |
243 | + } |
244 | + |
245 | + impl Ed25519PublicKey { |
246 | + pub(crate) fn verifying_key_from_bytes( |
247 | + bytes: &[u8], |
248 | + ) -> Result<Box<dyn VerifyingKey + Send + Sync>> { |
249 | + Ok(Box::new(Ed25519PublicKey { |
250 | + inner: ed25519_dalek::PublicKey::from_bytes(bytes) |
251 | .map_err(|err| Error::CryptoError(err.to_string()))?, |
252 | - }, |
253 | - }) |
254 | + })) |
255 | + } |
256 | } |
257 | - } |
258 | |
259 | - impl SigningKey for Ed25519Key { |
260 | - type Hasher = Sha256; |
261 | + impl VerifyingKey for Ed25519PublicKey { |
262 | + fn verify<'a>( |
263 | + &self, |
264 | + headers: &mut dyn Iterator<Item = (&'a [u8], &'a [u8])>, |
265 | + signature: &[u8], |
266 | + canonicalization: Canonicalization, |
267 | + algorithm: Algorithm, |
268 | + ) -> Result<()> { |
269 | + if !matches!(algorithm, Algorithm::Ed25519Sha256) { |
270 | + return Err(Error::IncompatibleAlgorithms); |
271 | + } |
272 | + |
273 | + let hash = canonicalization.hash_headers::<Sha256>(headers); |
274 | + self.inner |
275 | + .verify_strict( |
276 | + hash.as_ref(), |
277 | + &ed25519_dalek::Signature::from_bytes(signature) |
278 | + .map_err(|err| Error::CryptoError(err.to_string()))?, |
279 | + ) |
280 | + .map_err(|_| Error::FailedVerification) |
281 | + } |
282 | + } |
283 | + |
284 | + impl Writer for sha1::Sha1 { |
285 | + fn write(&mut self, buf: &[u8]) { |
286 | + self.update(buf); |
287 | + } |
288 | + } |
289 | + |
290 | + impl Writer for sha2::Sha256 { |
291 | + fn write(&mut self, buf: &[u8]) { |
292 | + self.update(buf); |
293 | + } |
294 | + } |
295 | + |
296 | + impl HashImpl for Sha1 { |
297 | + type Context = sha1::Sha1; |
298 | + |
299 | + fn hasher() -> Self::Context { |
300 | + <Self::Context as Digest>::new() |
301 | + } |
302 | + } |
303 | + |
304 | + impl HashImpl for Sha256 { |
305 | + type Context = sha2::Sha256; |
306 | + |
307 | + fn hasher() -> Self::Context { |
308 | + <Self::Context as Digest>::new() |
309 | + } |
310 | + } |
311 | |
312 | - fn sign(&self, data: HashOutput) -> Result<Vec<u8>> { |
313 | - Ok(self.inner.sign(data.as_ref()).to_bytes().to_vec()) |
314 | + impl HashContext for sha1::Sha1 { |
315 | + fn finish(self) -> HashOutput { |
316 | + HashOutput::Sha1(self.finalize()) |
317 | + } |
318 | } |
319 | |
320 | - fn algorithm(&self) -> Algorithm { |
321 | - Algorithm::Ed25519Sha256 |
322 | + impl HashContext for sha2::Sha256 { |
323 | + fn finish(self) -> HashOutput { |
324 | + HashOutput::Sha256(self.finalize()) |
325 | + } |
326 | } |
327 | } |
328 | |
329 | + pub use rust_crypto::{Ed25519Key, RsaKey}; |
330 | + pub(crate) use rust_crypto::{Ed25519PublicKey, RsaPublicKey}; |
331 | + |
332 | pub trait VerifyingKey { |
333 | fn verify<'a>( |
334 | &self, |
335 | @@ -142,118 +288,10 @@ impl VerifyingKeyType { |
336 | } |
337 | } |
338 | |
339 | - pub(crate) struct RsaPublicKey { |
340 | - inner: rsa::RsaPublicKey, |
341 | - } |
342 | - |
343 | - impl RsaPublicKey { |
344 | - fn verifying_key_from_bytes(bytes: &[u8]) -> Result<Box<dyn VerifyingKey + Send + Sync>> { |
345 | - Ok(Box::new(RsaPublicKey { |
346 | - inner: <rsa::RsaPublicKey as rsa::pkcs8::DecodePublicKey>::from_public_key_der(bytes) |
347 | - .or_else(|_| rsa::pkcs1::DecodeRsaPublicKey::from_pkcs1_der(bytes)) |
348 | - .map_err(|err| Error::CryptoError(err.to_string()))?, |
349 | - })) |
350 | - } |
351 | - } |
352 | - |
353 | - impl VerifyingKey for RsaPublicKey { |
354 | - fn verify<'a>( |
355 | - &self, |
356 | - headers: &mut dyn Iterator<Item = (&'a [u8], &'a [u8])>, |
357 | - signature: &[u8], |
358 | - canonicalization: Canonicalization, |
359 | - algorithm: Algorithm, |
360 | - ) -> Result<()> { |
361 | - match algorithm { |
362 | - Algorithm::RsaSha256 => { |
363 | - let hash = canonicalization.hash_headers::<Sha256>(headers); |
364 | - self.inner |
365 | - .verify( |
366 | - PaddingScheme::new_pkcs1v15_sign::<sha2::Sha256>(), |
367 | - hash.as_ref(), |
368 | - signature, |
369 | - ) |
370 | - .map_err(|_| Error::FailedVerification) |
371 | - } |
372 | - Algorithm::RsaSha1 => { |
373 | - let hash = canonicalization.hash_headers::<Sha1>(headers); |
374 | - self.inner |
375 | - .verify( |
376 | - PaddingScheme::new_pkcs1v15_sign::<sha1::Sha1>(), |
377 | - hash.as_ref(), |
378 | - signature, |
379 | - ) |
380 | - .map_err(|_| Error::FailedVerification) |
381 | - } |
382 | - Algorithm::Ed25519Sha256 => Err(Error::IncompatibleAlgorithms), |
383 | - } |
384 | - } |
385 | - } |
386 | - |
387 | - pub(crate) struct Ed25519PublicKey { |
388 | - inner: ed25519_dalek::PublicKey, |
389 | - } |
390 | - |
391 | - impl Ed25519PublicKey { |
392 | - fn verifying_key_from_bytes(bytes: &[u8]) -> Result<Box<dyn VerifyingKey + Send + Sync>> { |
393 | - Ok(Box::new(Ed25519PublicKey { |
394 | - inner: ed25519_dalek::PublicKey::from_bytes(bytes) |
395 | - .map_err(|err| Error::CryptoError(err.to_string()))?, |
396 | - })) |
397 | - } |
398 | - } |
399 | - |
400 | - impl VerifyingKey for Ed25519PublicKey { |
401 | - fn verify<'a>( |
402 | - &self, |
403 | - headers: &mut dyn Iterator<Item = (&'a [u8], &'a [u8])>, |
404 | - signature: &[u8], |
405 | - canonicalization: Canonicalization, |
406 | - algorithm: Algorithm, |
407 | - ) -> Result<()> { |
408 | - if !matches!(algorithm, Algorithm::Ed25519Sha256) { |
409 | - return Err(Error::IncompatibleAlgorithms); |
410 | - } |
411 | - |
412 | - let hash = canonicalization.hash_headers::<Sha256>(headers); |
413 | - self.inner |
414 | - .verify_strict( |
415 | - hash.as_ref(), |
416 | - &ed25519_dalek::Signature::from_bytes(signature) |
417 | - .map_err(|err| Error::CryptoError(err.to_string()))?, |
418 | - ) |
419 | - .map_err(|_| Error::FailedVerification) |
420 | - } |
421 | - } |
422 | - |
423 | - impl Writer for sha1::Sha1 { |
424 | - fn write(&mut self, buf: &[u8]) { |
425 | - self.update(buf); |
426 | - } |
427 | - } |
428 | - |
429 | - impl Writer for sha2::Sha256 { |
430 | - fn write(&mut self, buf: &[u8]) { |
431 | - self.update(buf); |
432 | - } |
433 | - } |
434 | - |
435 | pub trait HashContext: Writer + Sized { |
436 | fn finish(self) -> HashOutput; |
437 | } |
438 | |
439 | - impl HashContext for sha1::Sha1 { |
440 | - fn finish(self) -> HashOutput { |
441 | - HashOutput::Sha1(self.finalize()) |
442 | - } |
443 | - } |
444 | - |
445 | - impl HashContext for sha2::Sha256 { |
446 | - fn finish(self) -> HashOutput { |
447 | - HashOutput::Sha256(self.finalize()) |
448 | - } |
449 | - } |
450 | - |
451 | pub trait HashImpl { |
452 | type Context: HashContext; |
453 | |
454 | @@ -263,25 +301,9 @@ pub trait HashImpl { |
455 | #[derive(Clone, Copy)] |
456 | pub struct Sha1; |
457 | |
458 | - impl HashImpl for Sha1 { |
459 | - type Context = sha1::Sha1; |
460 | - |
461 | - fn hasher() -> Self::Context { |
462 | - <Self::Context as Digest>::new() |
463 | - } |
464 | - } |
465 | - |
466 | #[derive(Clone, Copy)] |
467 | pub struct Sha256; |
468 | |
469 | - impl HashImpl for Sha256 { |
470 | - type Context = sha2::Sha256; |
471 | - |
472 | - fn hasher() -> Self::Context { |
473 | - <Self::Context as Digest>::new() |
474 | - } |
475 | - } |
476 | - |
477 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
478 | #[repr(u64)] |
479 | pub enum HashAlgorithm { |