rc5_rs/
word.rs

1use {
2  crate::error::Error,
3  num::{
4    traits::{WrappingAdd, WrappingSub},
5    Num,
6    NumCast,
7    PrimInt,
8  },
9  secrecy::Zeroize,
10  std::{fmt::Debug, mem::size_of, ops::BitXor},
11};
12
13/// RC5 Word
14///
15/// This is one of the parameters of the RC5 algorithm. It is the size
16/// of the words that the algorithm operates on. By default all sensible
17/// word sizes are implemented (16/32/64/128).
18///
19/// If you want to use a different word size to support more exotic
20/// architectures, you can implement this trait for the type that you want
21/// to use as a word.
22///
23/// the following operations are required to be implemented by any type
24/// that wants to act as a word:
25///   - zero
26///   - bitor
27///   - bitxor
28///   - wrapping_add
29///   - wrapping_sub
30///   - rotate_left
31///   - rotate_right
32/// 	- shr
33/// 	- shl
34///
35/// Luckily all those methods are implemented for all Rust primitive types by
36/// the num crate, so you can just use those.
37///
38/// Also the type must calculate it's P and Q magic numbers according to
39/// this formula:
40///
41/// P = Odd((e − 2)2^w)
42/// Q = Odd((φ − 1)2^w)
43///
44/// where e is the base of natural logarithms and φ is the golden ratio and w is
45/// the word size.
46pub trait Word:
47  Num + BitXor + WrappingAdd + WrappingSub + PrimInt + NumCast + Debug + Zeroize
48where
49  Self: Sized,
50{
51  /// This is the W parameter of the RC5 algorithm.
52  const BITS: usize = size_of::<Self>() * 8;
53
54  const P: Self; // Odd((e − 2)2^w)
55  const Q: Self; // Odd((φ − 1)2^w)
56
57  /// Converts a little endian byte slice to a word
58  fn from_le_bytes(bytes: &[u8]) -> Result<Self, Error>;
59
60  /// Converts a word to a little endian byte slice
61  fn to_le_bytes(&self) -> Vec<u8>;
62}
63
64macro_rules! impl_word {
65  ($typ:tt, $q:expr, $p:expr) => {
66    impl Word for $typ {
67      const P: $typ = $p;
68      const Q: $typ = $q;
69
70      fn from_le_bytes(bytes: &[u8]) -> Result<Self, Error> {
71        if bytes.len() != size_of::<Self>() {
72          return Err(Error::InvalidBytes);
73        }
74
75        Ok($typ::from_le_bytes(
76          bytes.try_into().map_err(|_| Error::InvalidBytes)?,
77        ))
78      }
79
80      fn to_le_bytes(&self) -> Vec<u8> {
81        $typ::to_le_bytes(*self).to_vec()
82      }
83    }
84  };
85}
86
87impl_word!(u16, 0x9E37, 0xB7E1);
88impl_word!(u32, 0x9E3779B9, 0xB7E15163);
89impl_word!(u64, 0x9E3779B97F4A7C15, 0xB7E151628AED2A6B);
90impl_word!(
91  u128,
92  0x9E3779B97F4A7C15F39CC0605CEDC835,
93  0xB7E151628AED2A6ABF7158809CF4F3C7
94);