1use std::io;
2use std::cmp;
3use std::mem;
4use std::fmt;
5
6use buffered_reader::BufferedReader;
7use buffered_reader::buffered_reader_generic_read_impl;
8
9use crate::fmt::hex;
10use crate::packet::Signature;
11use crate::parse::{Cookie, HashesFor, Hashing};
12use crate::Result;
13use crate::types::HashAlgorithm;
14use crate::types::SignatureType;
15
16const TRACE : bool = false;
17
18#[derive(Clone, Eq)]
23pub(crate) enum HashingMode<T> {
24 Binary(Vec<u8>, T),
28
29 Text(Vec<u8>, T),
33
34 TextLastWasCr(Vec<u8>, T),
37}
38
39impl<T: std::fmt::Debug> std::fmt::Debug for HashingMode<T> {
40 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
41 use self::HashingMode::*;
42 match self {
43 Binary(salt, t) if salt.is_empty() =>
44 write!(f, "Binary({:?})", t),
45 Binary(salt, t) =>
46 write!(f, "Binary({}, {:?})", hex::encode(salt), t),
47 Text(salt, t) if salt.is_empty() =>
48 write!(f, "Text({:?})", t),
49 Text(salt, t) =>
50 write!(f, "Text({}, {:?})", hex::encode(salt), t),
51 TextLastWasCr(salt, t) if salt.is_empty() =>
52 write!(f, "Text(last was CR, {:?})", t),
53 TextLastWasCr(salt, t) =>
54 write!(f, "Text(last was CR, {}, {:?})", hex::encode(salt), t),
55 }
56 }
57}
58
59impl<T: PartialEq> PartialEq for HashingMode<T> {
60 fn eq(&self, other: &Self) -> bool {
61 use self::HashingMode::*;
62 match (self, other) {
63 (Binary(salt_s, s), Binary(salt_o, o)) =>
64 salt_s == salt_o && s == o,
65
66 (Text(salt_s, s), Text(salt_o, o)) =>
67 salt_s == salt_o && s == o,
68 (TextLastWasCr(salt_s, s), Text(salt_o, o)) =>
69 salt_s == salt_o && s == o,
70 (Text(salt_s, s), TextLastWasCr(salt_o, o)) =>
71 salt_s == salt_o && s == o,
72 (TextLastWasCr(salt_s, s), TextLastWasCr(salt_o, o)) =>
73 salt_s == salt_o && s == o,
74
75 _ => false,
76 }
77 }
78}
79
80impl<T> HashingMode<T> {
81 pub(crate) fn map<U, F: Fn(&T) -> U>(&self, f: F) -> HashingMode<U> {
82 use self::HashingMode::*;
83 match self {
84 Binary(salt, t) => Binary(salt.clone(), f(t)),
85 Text(salt, t) => Text(salt.clone(), f(t)),
86 TextLastWasCr(salt, t) => TextLastWasCr(salt.clone(), f(t)),
87 }
88 }
89
90 pub(crate) fn mapf<U, F: Fn(T) -> Result<U>>(self, f: F)
91 -> Result<HashingMode<U>> {
92 use self::HashingMode::*;
93 match self {
94 Binary(salt, t) => Ok(Binary(salt.clone(), f(t)?)),
95 Text(salt, t) => Ok(Text(salt.clone(), f(t)?)),
96 TextLastWasCr(salt, t) => Ok(TextLastWasCr(salt.clone(), f(t)?)),
97 }
98 }
99
100 pub(crate) fn salt(&self) -> &[u8] {
101 use self::HashingMode::*;
102 match self {
103 Binary(salt, _t) => salt,
104 Text(salt, _t) => salt,
105 TextLastWasCr(salt, _t) => salt,
106 }
107 }
108
109 pub(crate) fn as_ref(&self) -> &T {
110 use self::HashingMode::*;
111 match self {
112 Binary(_salt, t) => t,
113 Text(_salt, t) => t,
114 TextLastWasCr(_salt, t) => t,
115 }
116 }
117
118 pub(crate) fn as_mut(&mut self) -> &mut T {
119 use self::HashingMode::*;
120 match self {
121 Binary(_salt, t) => t,
122 Text(_salt, t) => t,
123 TextLastWasCr(_salt, t) => t,
124 }
125 }
126
127 pub(crate) fn for_signature(t: T, s: &Signature) -> Self {
128 match s {
129 Signature::V3(s) => Self::for_salt_and_type(t, &[], s.typ()),
130 Signature::V4(s) => Self::for_salt_and_type(t, &[], s.typ()),
131 Signature::V6(s) => Self::for_salt_and_type(t, s.salt(), s.typ()),
132 }
133 }
134 pub(crate) fn for_salt_and_type(t: T, salt: &[u8], typ: SignatureType)
135 -> Self
136 {
137 if typ == SignatureType::Text {
138 HashingMode::Text(salt.into(), t)
139 } else {
140 HashingMode::Binary(salt.into(), t)
141 }
142 }
143
144 pub(crate) fn into_inner(self) -> T {
145 use self::HashingMode::*;
146 match self {
147 Binary(_salt, t) => t,
148 Text(_salt, t) => t,
149 TextLastWasCr(_salt, t) => t,
150 }
151 }
152}
153
154impl HashingMode<crate::crypto::hash::Context>
155{
156 pub(crate) fn update(&mut self, data: &[u8]) {
159 if data.is_empty() {
160 return;
164 }
165
166 let (h, mut last_was_cr) = match self {
167 HashingMode::Text(_salt, h) => (h, false),
168 HashingMode::TextLastWasCr(_salt, h) => (h, true),
169 HashingMode::Binary(_salt, h) => return h.update(data),
170 };
171
172 let mut line = data;
173 let last_is_cr = line.last() == Some(&b'\r');
174 while ! line.is_empty() {
175 let mut next = 0;
176 for (i, c) in line.iter().cloned().enumerate() {
177 match c {
178 b'\n' if last_was_cr => {
179 assert_eq!(i, 0);
181 assert_eq!(next, 0);
182 next = 1;
183 break;
184 },
185 b'\r' | b'\n' => {
186 h.update(&line[..i]);
187 h.update(b"\r\n");
188 next = i + 1;
189 if c == b'\r' && line.get(next) == Some(&b'\n') {
190 next += 1;
191 }
192 break;
193 },
194 _ => (),
195 }
196 last_was_cr = false;
197 }
198
199 if next > 0 {
200 line = &line[next..];
201 } else {
202 h.update(line);
203 break;
204 }
205 }
206
207 match (&mut *self, last_is_cr) {
208 (&mut HashingMode::Text(_, _), false) => {
209 },
214 (&mut HashingMode::Text(ref mut salt, ref mut h), true) => {
215 *self =
216 HashingMode::TextLastWasCr(std::mem::take(salt), h.clone());
217 }
218 (&mut HashingMode::TextLastWasCr(ref mut salt, ref mut h), false) =>
219 {
220 *self = HashingMode::Text(std::mem::take(salt), h.clone());
221 },
222 (&mut HashingMode::TextLastWasCr(_, _), true) => (),
223
224 _ => unreachable!("handled above"),
225 }
226 }
227}
228
229pub(crate) struct HashedReader<R: BufferedReader<Cookie>> {
230 reader: R,
231 cookie: Cookie,
232}
233
234impl<R: BufferedReader<Cookie>> fmt::Display for HashedReader<R> {
235 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236 write!(f, "HashedReader")
237 }
238}
239
240impl<R: BufferedReader<Cookie>> fmt::Debug for HashedReader<R> {
241 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
242 f.debug_struct("HashedReader")
243 .field("cookie", &self.cookie)
244 .field("reader", &self.reader)
245 .finish()
246 }
247}
248
249impl<R: BufferedReader<Cookie>> HashedReader<R> {
250 pub fn new(reader: R, hashes_for: HashesFor,
254 algos: Vec<HashingMode<HashAlgorithm>>)
255 -> Result<Self> {
256 let mut cookie = Cookie::default();
257
258 for mode in algos {
259 let salt = mode.salt().to_vec();
260 let mode = mode.mapf(|algo| {
261 let mut ctx = algo.context()?
262 .for_digest();
266 ctx.update(&salt);
267 Ok(ctx)
268 })?;
269
270 cookie.sig_group_mut().hashes.push(mode);
271 }
272
273 cookie.hashes_for = hashes_for;
274
275 Ok(HashedReader {
276 reader,
277 cookie,
278 })
279 }
280}
281
282impl Cookie {
283 fn hash_update(&mut self, data: &[u8]) {
284 let level = self.level.unwrap_or(0);
285 let hashes_for = self.hashes_for;
286 let ngroups = self.sig_groups.len();
287
288 tracer!(TRACE, "Cookie::hash_update", level);
289 t!("({} bytes, {} hashes, enabled: {:?})",
290 data.len(), self.sig_group().hashes.len(), self.hashing);
291
292 if self.hashes_for == HashesFor::CleartextSignature {
293 return self.hash_update_csf(data);
294 }
295
296 if let Some(stashed_data) = self.hash_stash.take() {
298 assert!(ngroups > 1);
306 for h in self.sig_groups[ngroups-2].hashes.iter_mut()
307 {
308 t!("({:?}): group {} {:?} hashing {} stashed bytes.",
309 hashes_for, ngroups-2,
310 h.map(|ctx| ctx.algo()),
311 data.len());
312
313 h.update(&stashed_data);
314 }
315 }
316
317 if data.is_empty() {
318 return;
319 }
320
321 if self.hashing == Hashing::Disabled {
322 t!(" hash_update: NOT hashing {} bytes: {}.",
323 data.len(), crate::fmt::to_hex(data, true));
324 return;
325 }
326
327 let topmost_group = |i| i == ngroups - 1;
328 for (i, sig_group) in self.sig_groups.iter_mut().enumerate() {
329 if topmost_group(i) && self.hashing != Hashing::Enabled {
330 t!("topmost group {} NOT hashing {} bytes: {}.",
331 i, data.len(), crate::fmt::to_hex(data, true));
332
333 return;
334 }
335
336 for h in sig_group.hashes.iter_mut() {
337 t!("{:?}: group {} {:?} hashing {} bytes.",
338 hashes_for, i, h.map(|ctx| ctx.algo()), data.len());
339 h.update(data);
340 }
341 }
342 }
343
344 fn hash_update_csf(&mut self, data: &[u8]) {
345 let level = self.level.unwrap_or(0);
346 let hashes_for = self.hashes_for;
347 let ngroups = self.sig_groups.len();
348
349 assert_eq!(self.hashes_for, HashesFor::CleartextSignature);
350 assert!(ngroups == 1 || ngroups == 2);
356
357 tracer!(TRACE, "Cookie::hash_update_csf", level);
358 t!("Cleartext Signature Framework message");
359
360 if data.is_empty() {
361 return;
362 }
363
364 if self.hashing == Hashing::Disabled {
365 t!(" hash_update: NOT hashing {} bytes: {}.",
366 data.len(), crate::fmt::to_hex(data, true));
367 return;
368 }
369
370 for h in self.sig_groups[0].hashes.iter_mut() {
372 t!("{:?}: {:?} hashing {} bytes.",
373 hashes_for, h.map(|ctx| ctx.algo()), data.len());
374 h.update(data);
375 }
376 }
377}
378
379impl<T: BufferedReader<Cookie>> io::Read for HashedReader<T> {
380 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
381 buffered_reader_generic_read_impl(self, buf)
382 }
383}
384
385impl<R: BufferedReader<Cookie>>
388 BufferedReader<Cookie> for HashedReader<R> {
389 fn buffer(&self) -> &[u8] {
390 self.reader.buffer()
391 }
392
393 fn data(&mut self, amount: usize) -> io::Result<&[u8]> {
394 self.reader.data(amount)
395 }
396
397 fn data_hard(&mut self, amount: usize) -> io::Result<&[u8]> {
398 self.reader.data_hard(amount)
399 }
400
401 fn consume(&mut self, amount: usize) -> &[u8] {
402 let mut state = self.cookie_set(Cookie::default());
406
407 {
408 let data = self.reader.buffer();
413 assert!(data.len() >= amount);
414 state.hash_update(&data[..amount]);
415 }
416
417 self.cookie_set(state);
418
419 self.reader.consume(amount)
420 }
421
422 fn data_consume(&mut self, amount: usize) -> io::Result<&[u8]> {
423 let mut state = self.cookie_set(Cookie::default());
427
428 let got = {
429 let data = self.reader.data(amount)?;
430 let data = &data[..cmp::min(data.len(), amount)];
431 state.hash_update(data);
432 data.len()
433 };
434
435 self.cookie_set(state);
436
437 if let Ok(data) = self.reader.data_consume(amount) {
438 assert!(data.len() >= got);
439 Ok(data)
440 } else {
441 panic!("reader.data_consume() returned less than reader.data()!");
442 }
443 }
444
445 fn data_consume_hard(&mut self, amount: usize) -> io::Result<&[u8]> {
446 let mut state = self.cookie_set(Cookie::default());
450
451 {
452 let data = self.reader.data_hard(amount)?;
453 assert!(data.len() >= amount);
454 state.hash_update(&data[..amount]);
455 }
456
457 self.cookie_set(state);
458
459 let result = self.reader.data_consume(amount);
460 assert!(result.is_ok());
461 result
462 }
463
464 fn get_mut(&mut self) -> Option<&mut dyn BufferedReader<Cookie>> {
465 Some(&mut self.reader)
466 }
467
468 fn get_ref(&self) -> Option<&dyn BufferedReader<Cookie>> {
469 Some(&self.reader)
470 }
471
472 fn into_inner<'b>(self: Box<Self>)
473 -> Option<Box<dyn BufferedReader<Cookie> + 'b>>
474 where Self: 'b {
475 Some(self.reader.into_boxed())
476 }
477
478 fn cookie_set(&mut self, cookie: Cookie) -> Cookie {
479 mem::replace(&mut self.cookie, cookie)
480 }
481
482 fn cookie_ref(&self) -> &Cookie {
483 &self.cookie
484 }
485
486 fn cookie_mut(&mut self) -> &mut Cookie {
487 &mut self.cookie
488 }
489}
490
491pub(crate) fn hash_buffered_reader<R>(reader: R,
498 algos: &[HashingMode<HashAlgorithm>])
499 -> Result<Vec<HashingMode<crate::crypto::hash::Context>>>
500 where R: BufferedReader<crate::parse::Cookie>,
501{
502 let mut reader
503 = HashedReader::new(reader, HashesFor::Signature, algos.to_vec())?;
504
505 reader.drop_eof()?;
507
508 let hashes =
509 mem::take(&mut reader.cookie_mut().sig_group_mut().hashes);
510 Ok(hashes)
511}
512
513#[cfg(test)]
514mod test {
515 use super::*;
516
517 use buffered_reader::BufferedReader;
518
519 #[test]
520 fn hash_test_1() {
521 use std::collections::HashMap;
522 struct Test<'a> {
523 data: &'a [u8],
524 expected: HashMap<HashAlgorithm, &'a str>,
525 }
526
527 let tests = [
528 Test {
529 data: &b"foobar\n"[..],
530 expected: [
531 (HashAlgorithm::SHA1,
532 "988881adc9fc3655077dc2d4d757d480b5ea0e11"),
533 ].iter().cloned().collect(),
534 },
535 Test {
536 data: &b"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\n"[..],
537 expected: [
538 (HashAlgorithm::SHA1,
539 "1d12c55b3a85daab4776a1df41a8f30ada099e11"),
540 (HashAlgorithm::SHA224,
541 "a4c1bde77c682a0e9e30c6afdd1ece2397ffeec61dde2a0eaa23191e"),
542 (HashAlgorithm::SHA256,
543 "151a1d51a1870dc244f07f4844f46ee65fae19a8efeb60b203a074aff899e27d"),
544 (HashAlgorithm::SHA384,
545 "5bea68c8c696bbed95e152d61c446ad0e05bf68f7df39cbfeae568bee6f6691c840fb1d5dd2599737b08dbb33eed344b"),
546 (HashAlgorithm::SHA512,
547 "5fa032487774082af5cc833c2db5f943e31cc75cd2bfaa7d9bbd0ccabf5403b6dbcb484254727a524588f20e9ef336d8ce8533332c5ac1b9d50af3003a0da8d8"),
548 ].iter().filter(|(hash, _)| hash.is_supported()).cloned().collect(),
549 },
550 ];
551
552 for test in tests.iter() {
553 let reader
554 = buffered_reader::Generic::with_cookie(
555 test.data, None, Default::default());
556 let mut reader
557 = HashedReader::new(reader, HashesFor::MDC,
558 test.expected.keys().cloned()
559 .map(|v| HashingMode::Binary(vec![], v))
560 .collect()).unwrap();
561
562 assert_eq!(reader.steal_eof().unwrap(), test.data);
563
564 let cookie = reader.cookie_mut();
565
566 let mut hashes = std::mem::take(&mut cookie.sig_group_mut().hashes);
567 for mode in hashes.iter_mut() {
568 let hash = mode.as_mut();
569 let algo = hash.algo();
570 let mut digest = vec![0u8; hash.digest_size()];
571 let _ = hash.digest(&mut digest);
572
573 assert_eq!(digest,
574 &crate::fmt::from_hex(test.expected.get(&algo)
575 .unwrap(), true)
576 .unwrap()[..],
577 "Algo: {:?}", algo);
578 }
579 }
580 }
581
582 #[test]
583 fn hash_update_text() -> crate::Result<()> {
584 for text in &[
585 "one\r\ntwo\r\nthree",
586 "one\ntwo\nthree",
587 "one\rtwo\rthree",
588 "one\ntwo\r\nthree",
589 ] {
590 for chunk_size in &[ text.len(), 1 ] {
591 let mut ctx
592 = HashingMode::Text(vec![],
593 HashAlgorithm::SHA256.context()?
594 .for_digest());
595 for chunk in text.as_bytes().chunks(*chunk_size) {
596 ctx.update(chunk);
597 }
598 let mut ctx = ctx.into_inner();
599 let mut digest = vec![0; ctx.digest_size()];
600 let _ = ctx.digest(&mut digest);
601 assert_eq!(
602 &crate::fmt::hex::encode(&digest),
603 "5536758151607BB81CE8D6F49189B2E84763DA9EA84965AB7327E704DAE415EB",
604 "{:?}, chunk size: {}", text, chunk_size);
605 }
606 }
607 Ok(())
608 }
609
610 #[test]
611 fn hash_reader_test() {
612 use std::collections::HashMap;
613
614 let expected: HashMap<HashAlgorithm, &str> = [
615 (HashAlgorithm::SHA1, "7945E3DA269C25C04F9EF435A5C0F25D9662C771"),
616 (HashAlgorithm::SHA512, "DDE60DB05C3958AF1E576CD006A7F3D2C343DD8C\
617 8DECE789A15D148DF90E6E0D1454DE734F834350\
618 2CA93759F22C8F6221BE35B6BDE9728BD12D2891\
619 22437CB1"),
620 ].iter().cloned().collect();
621
622 let reader
623 = buffered_reader::Generic::with_cookie(
624 std::io::Cursor::new(crate::tests::manifesto()),
625 None, Default::default());
626 let result =
627 hash_buffered_reader(
628 reader,
629 &expected.keys().cloned()
630 .map(|v| HashingMode::Binary(vec![], v)).
631 collect::<Vec<_>>())
632 .unwrap();
633
634 for mut mode in result.into_iter() {
635 let hash = mode.as_mut();
636 let algo = hash.algo();
637 let mut digest = vec![0u8; hash.digest_size()];
638 let _ = hash.digest(&mut digest);
639
640 assert_eq!(*expected.get(&algo).unwrap(),
641 &crate::fmt::to_hex(&digest[..], false));
642 }
643 }
644}