1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#[allow(clippy::unit_arg)]
mod any;
mod hash;
mod keyed;
mod tag;

use crate::error::*;
use crate::xoodoo::*;

pub use any::*;
pub use hash::*;
pub use keyed::*;
pub use tag::*;

pub(crate) const HASH_ABSORB_RATE: usize = 16;
pub(crate) const HASH_SQUEEZE_RATE: usize = 16;
pub(crate) const KEYED_ABSORB_RATE: usize = 44;
pub(crate) const KEYED_SQUEEZE_RATE: usize = 24;
pub(crate) const RATCHET_RATE: usize = 16;

mod internal {
    use super::*;

    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
    pub enum Mode {
        Hash,
        Keyed,
    }

    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
    pub enum Phase {
        Up,
        Down,
    }

    pub trait XoodyakCommon {
        fn state(&mut self) -> &mut Xoodoo;
        fn mode(&self) -> Mode;
        fn phase(&self) -> Phase;
        fn set_phase(&mut self, phase: Phase);
        fn absorb_rate(&self) -> usize;
        fn squeeze_rate(&self) -> usize;

        #[inline(always)]
        fn permute(&mut self) {
            self.state().permute()
        }

        #[inline(always)]
        fn add_byte(&mut self, byte: u8, offset: usize) {
            self.state().add_byte(byte, offset);
        }

        #[inline(always)]
        fn add_bytes(&mut self, bytes: &[u8]) {
            self.state().add_bytes(bytes);
        }

        #[inline(always)]
        fn extract_bytes(&mut self, out: &mut [u8]) {
            self.state().extract_bytes(out);
        }

        #[inline(always)]
        fn up(&mut self, out: Option<&mut [u8]>, cu: u8) {
            debug_assert!(out.as_ref().map(|x| x.len()).unwrap_or(0) <= self.squeeze_rate());
            self.set_phase(Phase::Up);
            if self.mode() != Mode::Hash {
                self.add_byte(cu, 47);
            }
            self.permute();
            if let Some(mut out) = out {
                self.extract_bytes(&mut out);
            }
        }

        #[inline(always)]
        fn down(&mut self, bin: Option<&[u8]>, cd: u8) {
            debug_assert!(bin.as_ref().map(|x| x.len()).unwrap_or(0) <= self.absorb_rate());
            self.set_phase(Phase::Down);
            if let Some(bin) = bin {
                self.add_bytes(&bin);
                self.add_byte(0x01, bin.len());
            } else {
                self.add_byte(0x01, 0);
            }
            if self.mode() == Mode::Hash {
                self.add_byte(cd & 0x01, 47);
            } else {
                self.add_byte(cd, 47);
            }
        }

        #[inline]
        fn absorb_any(&mut self, bin: &[u8], rate: usize, cd: u8) {
            let mut chunks_it = bin.chunks(rate);
            if self.phase() != Phase::Up {
                self.up(None, 0x00)
            }
            self.down(chunks_it.next(), cd);
            for chunk in chunks_it {
                self.up(None, 0x00);
                self.down(Some(chunk), 0x00);
            }
        }

        #[inline]
        fn squeeze_any(&mut self, out: &mut [u8], cu: u8) {
            let mut chunks_it = out.chunks_mut(self.squeeze_rate());
            self.up(chunks_it.next(), cu);
            for chunk in chunks_it {
                self.down(None, 0x00);
                self.up(Some(chunk), 0x00);
            }
        }
    }
}

pub trait XoodyakCommon: internal::XoodyakCommon {
    #[inline(always)]
    fn absorb(&mut self, bin: &[u8]) {
        self.absorb_any(bin, self.absorb_rate(), 0x03);
    }

    #[inline]
    fn absorb_more(&mut self, bin: &[u8], rate: usize) {
        for chunk in bin.chunks(rate) {
            self.up(None, 0x00);
            self.down(Some(chunk), 0x00);
        }
    }

    #[inline(always)]
    fn squeeze(&mut self, out: &mut [u8]) {
        self.squeeze_any(out, 0x40);
    }

    #[inline(always)]
    fn squeeze_key(&mut self, out: &mut [u8]) {
        self.squeeze_any(out, 0x20);
    }

    #[inline]
    fn squeeze_more(&mut self, out: &mut [u8]) {
        for chunk in out.chunks_mut(self.squeeze_rate()) {
            self.down(None, 0x00);
            self.up(Some(chunk), 0x00);
        }
    }

    #[cfg(feature = "std")]
    fn squeeze_to_vec(&mut self, len: usize) -> Vec<u8> {
        let mut out = vec![0u8; len];
        self.squeeze(&mut out);
        out
    }
}