1use rayon::prelude::*;
3use serde::{Deserialize, Serialize};
4use snafu::{ensure, OptionExt, ResultExt};
5use ssb_legacy_msg_data::{
6 json::{from_slice, to_vec},
7 value::Value,
8};
9use ssb_multiformats::multihash::Multihash;
10
11use crate::error::{
12 ActualHashDidNotMatchKey, AuthorsDidNotMatch, InvalidMessage,
13 InvalidMessageCouldNotSerializeValue, InvalidMessageNoValue, InvalidPreviousMessage, Result,
14};
15use crate::message_value::{message_value_common_checks, SsbMessageValue};
16use crate::utils;
17
18#[derive(Serialize, Deserialize, Debug)]
20pub struct SsbMessage {
21 pub key: Multihash,
22 pub value: SsbMessageValue,
23}
24
25pub fn validate_multi_author_message_hash_chain<T: AsRef<[u8]>>(message_bytes: T) -> Result<()> {
44 let message_bytes = message_bytes.as_ref();
45
46 let message = from_slice::<SsbMessage>(message_bytes).context(InvalidMessage {
47 message: message_bytes.to_owned(),
48 })?;
49
50 let message_value = message.value;
51
52 message_value_common_checks(&message_value, None, message_bytes, None, false)?;
53
54 let verifiable_msg: Value = from_slice(message_bytes).context(InvalidMessage {
55 message: message_bytes.to_owned(),
56 })?;
57
58 let verifiable_msg_value = match verifiable_msg {
60 Value::Object(ref o) => o.get("value").context(InvalidMessageNoValue)?,
61 _ => panic!(),
62 };
63
64 let value_bytes =
66 to_vec(verifiable_msg_value, false).context(InvalidMessageCouldNotSerializeValue)?;
67
68 let message_actual_multihash = utils::multihash_from_bytes(&value_bytes);
69
70 ensure!(
72 message_actual_multihash == message.key,
73 ActualHashDidNotMatchKey {
74 message: message_bytes.to_owned(),
75 actual_hash: message_actual_multihash,
76 expected_hash: message.key,
77 }
78 );
79
80 Ok(())
81}
82
83pub fn par_validate_multi_author_message_hash_chain_of_feed<T: AsRef<[u8]>>(
90 messages: &[T],
91) -> Result<()>
92where
93 [T]: ParallelSlice<T>,
94 T: Sync,
95{
96 messages
97 .par_iter()
98 .enumerate()
99 .try_fold(
100 || (),
101 |_, (_idx, msg)| validate_multi_author_message_hash_chain(msg.as_ref()),
102 )
103 .try_reduce(|| (), |_, _| Ok(()))
104}
105
106pub fn validate_ooo_message_hash_chain<T: AsRef<[u8]>, U: AsRef<[u8]>>(
122 message_bytes: T,
123 previous_msg_bytes: Option<U>,
124) -> Result<()> {
125 let message_bytes = message_bytes.as_ref();
126
127 let (previous_value, _previous_key) = match previous_msg_bytes {
128 Some(message) => {
129 let previous =
130 from_slice::<SsbMessage>(message.as_ref()).context(InvalidPreviousMessage {
131 message: message.as_ref().to_owned(),
132 })?;
133 (Some(previous.value), Some(previous.key))
134 }
135 None => (None, None),
136 };
137
138 let message = from_slice::<SsbMessage>(message_bytes).context(InvalidMessage {
139 message: message_bytes.to_owned(),
140 })?;
141
142 let message_value = message.value;
143
144 message_value_common_checks(&message_value, None, message_bytes, None, false)?;
145
146 if let Some(previous_value) = previous_value.as_ref() {
147 ensure!(
149 message_value.author == previous_value.author,
150 AuthorsDidNotMatch {
151 previous_author: previous_value.author.clone(),
152 author: message_value.author
153 }
154 );
155 }
156
157 let verifiable_msg: Value = from_slice(message_bytes).context(InvalidMessage {
158 message: message_bytes.to_owned(),
159 })?;
160
161 let verifiable_msg_value = match verifiable_msg {
163 Value::Object(ref o) => o.get("value").context(InvalidMessageNoValue)?,
164 _ => panic!(),
165 };
166
167 let value_bytes =
169 to_vec(verifiable_msg_value, false).context(InvalidMessageCouldNotSerializeValue)?;
170
171 let message_actual_multihash = utils::multihash_from_bytes(&value_bytes);
172
173 ensure!(
175 message_actual_multihash == message.key,
176 ActualHashDidNotMatchKey {
177 message: message_bytes.to_owned(),
178 actual_hash: message_actual_multihash,
179 expected_hash: message.key,
180 }
181 );
182
183 Ok(())
184}
185
186pub fn par_validate_ooo_message_hash_chain_of_feed<T: AsRef<[u8]>>(messages: &[T]) -> Result<()>
193where
194 [T]: ParallelSlice<T>,
195 T: Sync,
196{
197 messages
198 .par_iter()
199 .enumerate()
200 .try_fold(
201 || (),
202 |_, (idx, msg)| {
203 if idx == 0 {
204 validate_ooo_message_hash_chain::<_, &[u8]>(msg.as_ref(), None)
205 } else {
206 validate_ooo_message_hash_chain(msg.as_ref(), Some(messages[idx - 1].as_ref()))
207 }
208 },
209 )
210 .try_reduce(|| (), |_, _| Ok(()))
211}
212
213pub fn par_validate_message_hash_chain_of_feed<T: AsRef<[u8]>, U: AsRef<[u8]>>(
271 messages: &[T],
272 previous: Option<U>,
273) -> Result<()>
274where
275 [T]: ParallelSlice<T>,
276 T: Sync,
277 U: Sync + Send + Copy,
278{
279 messages
280 .par_iter()
281 .enumerate()
282 .try_fold(
283 || (),
284 |_, (idx, msg)| {
285 if idx == 0 {
286 let prev = previous.map(|prev| prev.as_ref().to_owned());
287 validate_message_hash_chain(msg.as_ref(), prev)
288 } else {
289 validate_message_hash_chain(msg.as_ref(), Some(messages[idx - 1].as_ref()))
290 }
291 },
292 )
293 .try_reduce(|| (), |_, _| Ok(()))
294}
295
296pub fn validate_message_hash_chain<T: AsRef<[u8]>, U: AsRef<[u8]>>(
360 message_bytes: T,
361 previous_msg_bytes: Option<U>,
362) -> Result<()> {
363 let message_bytes = message_bytes.as_ref();
364 let (previous_value, previous_key) = match previous_msg_bytes {
366 Some(message) => {
367 let previous =
368 from_slice::<SsbMessage>(message.as_ref()).context(InvalidPreviousMessage {
369 message: message.as_ref().to_owned(),
370 })?;
371 (Some(previous.value), Some(previous.key))
372 }
373
374 None => (None, None),
375 };
376
377 let message = from_slice::<SsbMessage>(message_bytes).context(InvalidMessage {
378 message: message_bytes.to_owned(),
379 })?;
380
381 let message_value = message.value;
382
383 message_value_common_checks(
384 &message_value,
385 previous_value.as_ref(),
386 message_bytes,
387 previous_key.as_ref(),
388 true,
390 )?;
391
392 let verifiable_msg: Value = from_slice(message_bytes).context(InvalidMessage {
393 message: message_bytes.to_owned(),
394 })?;
395
396 let verifiable_msg_value = match verifiable_msg {
398 Value::Object(ref o) => o.get("value").context(InvalidMessageNoValue)?,
399 _ => panic!(),
400 };
401
402 let value_bytes =
404 to_vec(verifiable_msg_value, false).context(InvalidMessageCouldNotSerializeValue)?;
405
406 let message_actual_multihash = utils::multihash_from_bytes(&value_bytes);
407
408 ensure!(
410 message_actual_multihash == message.key,
411 ActualHashDidNotMatchKey {
412 message: message_bytes.to_owned(),
413 actual_hash: message_actual_multihash,
414 expected_hash: message.key,
415 }
416 );
417
418 Ok(())
419}
420
421#[cfg(test)]
422mod tests {
423 use crate::error::Error;
424 use crate::message::{
425 par_validate_message_hash_chain_of_feed,
426 par_validate_multi_author_message_hash_chain_of_feed,
427 par_validate_ooo_message_hash_chain_of_feed, validate_message_hash_chain,
428 validate_multi_author_message_hash_chain, validate_ooo_message_hash_chain,
429 };
430 use crate::test_data::*;
431
432 #[test]
433 fn it_works_multi_author() {
434 assert!(validate_multi_author_message_hash_chain(MESSAGE_2.as_bytes()).is_ok());
435 }
436
437 #[test]
438 fn it_works_ooo_messages_without_first_message() {
439 assert!(
440 validate_ooo_message_hash_chain(MESSAGE_2.as_bytes(), Some(MESSAGE_3.as_bytes()))
441 .is_ok()
442 );
443 }
444
445 #[test]
446 fn it_works_ooo_messages() {
447 assert!(
448 validate_ooo_message_hash_chain(MESSAGE_3.as_bytes(), Some(MESSAGE_1.as_bytes()))
449 .is_ok()
450 );
451 }
452
453 #[test]
454 fn it_validates_a_private_message_ooo() {
455 let result = validate_ooo_message_hash_chain::<_, &[u8]>(MESSAGE_PRIVATE.as_bytes(), None);
456
457 assert!(result.is_ok());
458 }
459
460 #[test]
461 fn it_detects_invalid_base64_for_private_message_ooo() {
462 let result =
463 validate_ooo_message_hash_chain::<_, &[u8]>(MESSAGE_PRIVATE_INVALID.as_bytes(), None);
464 match result {
465 Err(Error::InvalidBase64 { message: _ }) => {}
466 _ => panic!(),
467 }
468 }
469
470 #[test]
471 fn par_validate_multi_author_message_hash_chain_of_feed_works() {
472 let messages = [
473 MESSAGE_WITH_UNICODE.as_bytes(),
474 MESSAGE_PRIVATE.as_bytes(),
475 MESSAGE_1.as_bytes(),
476 ];
477
478 let result = par_validate_multi_author_message_hash_chain_of_feed(&messages[..]);
479 assert!(result.is_ok());
480 }
481
482 #[test]
483 fn par_validate_ooo_message_hash_chain_of_feed_with_first_message_works() {
484 let messages = [
485 MESSAGE_1.as_bytes(),
486 MESSAGE_3.as_bytes(),
487 MESSAGE_2.as_bytes(),
488 ];
489
490 let result = par_validate_ooo_message_hash_chain_of_feed(&messages[..]);
491 assert!(result.is_ok());
492 }
493
494 #[test]
495 fn par_validate_ooo_message_hash_chain_of_feed_without_first_message_works() {
496 let messages = [MESSAGE_3.as_bytes(), MESSAGE_2.as_bytes()];
497
498 let result = par_validate_ooo_message_hash_chain_of_feed(&messages[..]);
499 assert!(result.is_ok());
500 }
501
502 #[test]
503 fn it_works_first_message() {
504 assert!(validate_message_hash_chain::<_, &[u8]>(MESSAGE_1.as_bytes(), None).is_ok());
505 }
506
507 #[test]
508 fn it_works_second_message() {
509 assert!(
510 validate_message_hash_chain(MESSAGE_2.as_bytes(), Some(MESSAGE_1.as_bytes())).is_ok()
511 );
512 }
513
514 #[test]
515 fn par_validate_message_hash_chain_of_feed_first_messages_works() {
516 let messages = [MESSAGE_1.as_bytes(), MESSAGE_2.as_bytes()];
517
518 let result = par_validate_message_hash_chain_of_feed::<_, &[u8]>(&messages[..], None);
519 assert!(result.is_ok());
520 }
521
522 #[test]
523 fn par_validate_message_hash_chain_of_feed_with_prev_works() {
524 let messages = [MESSAGE_2.as_bytes(), MESSAGE_3.as_bytes()];
525
526 let result =
527 par_validate_message_hash_chain_of_feed(&messages[..], Some(MESSAGE_1.as_bytes()));
528 assert!(result.is_ok());
529 }
530
531 #[test]
532 fn first_message_must_have_previous_of_null() {
533 let result =
534 validate_message_hash_chain::<_, &[u8]>(MESSAGE_1_INVALID_PREVIOUS.as_bytes(), None);
535 match result {
536 Err(Error::FirstMessageDidNotHavePreviousOfNull { message: _ }) => {}
537 _ => panic!(),
538 }
539 }
540
541 #[test]
542 fn first_message_must_have_sequence_of_one() {
543 let result =
544 validate_message_hash_chain::<_, &[u8]>(MESSAGE_1_INVALID_SEQ.as_bytes(), None);
545 match result {
546 Err(Error::FirstMessageDidNotHaveSequenceOfOne { message: _ }) => {}
547 _ => panic!(),
548 }
549 }
550
551 #[test]
552 fn it_detects_incorrect_seq() {
553 let result = validate_message_hash_chain(
554 MESSAGE_2_INCORRECT_SEQUENCE.as_bytes(),
555 Some(MESSAGE_1.as_bytes()),
556 );
557 match result {
558 Err(Error::InvalidSequenceNumber {
559 message: _,
560 actual,
561 expected,
562 }) => {
563 assert_eq!(actual, 3);
564 assert_eq!(expected, 2);
565 }
566 _ => panic!(),
567 }
568 }
569
570 #[test]
571 fn it_detects_incorrect_author() {
572 let result = validate_message_hash_chain(
573 MESSAGE_2_INCORRECT_AUTHOR.as_bytes(),
574 Some(MESSAGE_1.as_bytes()),
575 );
576 match result {
577 Err(Error::AuthorsDidNotMatch {
578 previous_author: _,
579 author: _,
580 }) => {}
581 _ => panic!(),
582 }
583 }
584
585 #[test]
586 fn it_detects_incorrect_previous_of_null() {
587 let result = validate_message_hash_chain(
588 MESSAGE_2_PREVIOUS_NULL.as_bytes(),
589 Some(MESSAGE_1.as_bytes()),
590 );
591 match result {
592 Err(Error::PreviousWasNull) => {}
593 _ => panic!(),
594 }
595 }
596
597 #[test]
598 fn it_detects_incorrect_key() {
599 let result = validate_message_hash_chain(
600 MESSAGE_2_INCORRECT_KEY.as_bytes(),
601 Some(MESSAGE_1.as_bytes()),
602 );
603 match result {
604 Err(Error::ActualHashDidNotMatchKey {
605 message: _,
606 expected_hash: _,
607 actual_hash: _,
608 }) => {}
609 _ => panic!(),
610 }
611 }
612
613 #[test]
614 fn it_detects_incorrect_key_for_multi_author() {
615 let result = validate_multi_author_message_hash_chain(MESSAGE_2_INCORRECT_KEY.as_bytes());
616 match result {
617 Err(Error::ActualHashDidNotMatchKey {
618 message: _,
619 expected_hash: _,
620 actual_hash: _,
621 }) => {}
622 _ => panic!(),
623 }
624 }
625
626 #[test]
627 fn it_detects_extra_unwanted_field() {
628 let result =
629 validate_message_hash_chain::<_, &[u8]>(MESSAGE_WITH_EXTRA_FIELD.as_bytes(), None);
630 match result {
632 Err(Error::InvalidMessage {
633 source: _,
634 message: _,
635 }) => {}
636 _ => panic!(),
637 }
638 }
639
640 #[test]
641 fn it_detects_fork() {
642 let result =
643 validate_message_hash_chain(MESSAGE_2_FORK.as_bytes(), Some(MESSAGE_1.as_bytes()));
644 match result {
645 Err(Error::ForkedFeed { previous_seq: 1 }) => {}
646 _ => panic!(),
647 }
648 }
649
650 #[test]
651 fn it_detects_missing_hash_function() {
652 let result =
653 validate_message_hash_chain::<_, &[u8]>(MESSAGE_WITHOUT_HASH_FUNCTION.as_bytes(), None);
654 match result {
655 Err(Error::InvalidMessage {
656 source: _,
657 message: _,
658 }) => {}
659 _ => panic!(),
660 }
661 }
662
663 #[test]
664 fn it_detects_incorrect_hash_function() {
665 let result = validate_message_hash_chain::<_, &[u8]>(
666 MESSAGE_WITH_INVALID_HASH_FUNCTION.as_bytes(),
667 None,
668 );
669 match result {
670 Err(Error::InvalidHashFunction { message: _ }) => {}
671 _ => panic!(),
672 }
673 }
674
675 #[test]
676 fn it_validates_a_message_with_unicode() {
677 let result = validate_message_hash_chain(
678 MESSAGE_WITH_UNICODE.as_bytes(),
679 Some(MESSAGE_WITH_UNICODE_PREV.as_bytes()),
680 );
681
682 assert!(result.is_ok());
683 }
684
685 #[test]
686 fn it_detects_incorrect_message_value_order() {
687 let result = validate_message_hash_chain(
688 MESSAGE_2_INVALID_ORDER.as_bytes(),
689 Some(MESSAGE_1.as_bytes()),
690 );
691 match result {
692 Err(Error::InvalidMessageValueOrder { message: _ }) => {}
693 _ => panic!(),
694 }
695 }
696
697 #[test]
698 fn it_validates_a_private_message() {
699 let result = validate_message_hash_chain(
700 MESSAGE_PRIVATE.as_bytes(),
701 Some(MESSAGE_PRIVATE_PREV.as_bytes()),
702 );
703
704 assert!(result.is_ok());
705 }
706
707 #[test]
708 fn it_detects_invalid_base64_for_private_message() {
709 let result = validate_message_hash_chain(
710 MESSAGE_PRIVATE_INVALID.as_bytes(),
711 Some(MESSAGE_PRIVATE_PREV.as_bytes()),
712 );
713 match result {
714 Err(Error::InvalidBase64 { message: _ }) => {}
715 _ => panic!(),
716 }
717 }
718}