block_buffer/
lib.rs

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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
//! Fixed size buffer for block processing of data.
//!
//! # Examples
//! ```
//! use block_buffer::{EagerBuffer, array::typenum::U4};
//!
//! let mut buf = EagerBuffer::<U4>::default();
//!
//! let mut accum = Vec::new();
//! let msg1: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
//! let msg2: &[u8] = &[10, 11, 12];
//!
//! buf.digest_blocks(msg1, |blocks| accum.extend_from_slice(blocks));
//! buf.digest_blocks(msg2, |blocks| accum.extend_from_slice(blocks));
//!
//! assert_eq!(accum.len(), 3);
//! assert_eq!(accum[0], [0, 1, 2, 3]);
//! assert_eq!(accum[1], [4, 5, 6, 7]);
//! assert_eq!(accum[2], [8, 9, 10, 11]);
//!
//! let padded_block = buf.pad_with_zeros();
//! assert_eq!(padded_block, [12, 0, 0, 0]);
//! ```
//!
//! Note that block size used with buffers MUST be bigger than zero and smaller than 256.
//! You will get a compilation error with an invalid block size:
//!
//! ```compile_fail
//! use block_buffer::{EagerBuffer, array::typenum::U0};
//! let buf = EagerBuffer::<U0>::default();
//! ```
//! ```compile_fail
//! use block_buffer::{EagerBuffer, array::typenum::U256};
//! let buf = EagerBuffer::<U256>::default();
//! ```
#![no_std]
#![doc(
    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
)]
#![warn(missing_docs, rust_2018_idioms)]

pub use hybrid_array as array;

use array::{
    typenum::{Add1, B1},
    Array, ArraySize,
};
use core::{fmt, mem::MaybeUninit, ops::Add, ptr, slice};

#[cfg(feature = "zeroize")]
use zeroize::Zeroize;

mod read;
mod sealed;

pub use read::ReadBuffer;

/// Trait for buffer kinds.
pub trait BufferKind: sealed::Sealed {}

/// Eager block buffer kind, which guarantees that buffer position
/// always lies in the range of `0..BlockSize`.
#[derive(Copy, Clone, Debug, Default)]
pub struct Eager {}

/// Lazy block buffer kind, which guarantees that buffer position
/// always lies in the range of `0..=BlockSize`.
#[derive(Copy, Clone, Debug, Default)]
pub struct Lazy {}

impl BufferKind for Eager {}

impl BufferKind for Lazy {}

/// Eager block buffer.
pub type EagerBuffer<B> = BlockBuffer<B, Eager>;
/// Lazy block buffer.
pub type LazyBuffer<B> = BlockBuffer<B, Lazy>;

/// Block buffer error.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct Error;

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        f.write_str("Block buffer error")
    }
}

/// Buffer for block processing of data.
pub struct BlockBuffer<BS: ArraySize, K: BufferKind> {
    buffer: MaybeUninit<Array<u8, BS>>,
    pos: K::Pos,
}

impl<BS: ArraySize, K: BufferKind> BlockBuffer<BS, K> {
    /// This associated constant is used to assert block size correctness at compile time.
    const BLOCK_SIZE_ASSERT: bool = {
        if BS::USIZE == 0 {
            panic!("Block size can not be equal to zero!");
        }
        if BS::USIZE > 255 {
            panic!("Block size can not be bigger than 255!");
        }
        true
    };
}

impl<BS: ArraySize, K: BufferKind> Default for BlockBuffer<BS, K> {
    #[inline]
    fn default() -> Self {
        assert!(Self::BLOCK_SIZE_ASSERT);
        let mut buffer = MaybeUninit::uninit();
        let mut pos = Default::default();
        K::set_pos(&mut buffer, &mut pos, 0);
        Self { buffer, pos }
    }
}

impl<BS: ArraySize, K: BufferKind> Clone for BlockBuffer<BS, K> {
    #[inline]
    fn clone(&self) -> Self {
        // SAFETY: `BlockBuffer` does not implement `Drop` (i.e. it could be a `Copy` type),
        // so we can safely clone it using `ptr::read`.
        unsafe { ptr::read(self) }
    }
}

impl<BS: ArraySize, K: BufferKind> fmt::Debug for BlockBuffer<BS, K> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        f.debug_struct(K::NAME)
            .field("pos", &self.get_pos())
            .field("block_size", &BS::USIZE)
            .field("data", &self.get_data())
            .finish()
    }
}

impl<BS: ArraySize, K: BufferKind> BlockBuffer<BS, K> {
    /// Create new buffer from slice.
    ///
    /// # Panics
    /// If slice length is not valid for used buffer kind.
    #[inline(always)]
    pub fn new(buf: &[u8]) -> Self {
        Self::try_new(buf).unwrap()
    }

    /// Create new buffer from slice.
    ///
    /// Returns an error if slice length is not valid for used buffer kind.
    #[inline(always)]
    pub fn try_new(buf: &[u8]) -> Result<Self, Error> {
        assert!(Self::BLOCK_SIZE_ASSERT);
        if !K::invariant(buf.len(), BS::USIZE) {
            return Err(Error);
        }
        let mut res = Self::default();
        // SAFETY: we have checked that buffer length satisfies the buffer kind invariant
        unsafe {
            res.set_data_unchecked(buf);
        }
        Ok(res)
    }

    /// Digest data in `input` in blocks of size `BlockSize` using
    /// the `compress` function, which accepts slice of blocks.
    #[inline]
    pub fn digest_blocks(&mut self, mut input: &[u8], mut compress: impl FnMut(&[Array<u8, BS>])) {
        let pos = self.get_pos();
        // using `self.remaining()` for some reason
        // prevents panic elimination
        let rem = self.size() - pos;
        let n = input.len();
        // Note that checking condition `pos + n < BlockSize` is
        // equivalent to checking `n < rem`, where `rem` is equal
        // to `BlockSize - pos`. Using the latter allows us to work
        // around compiler accounting for possible overflow of
        // `pos + n` which results in it inserting unreachable
        // panic branches. Using `unreachable_unchecked` in `get_pos`
        // we convince compiler that `BlockSize - pos` never underflows.
        if K::invariant(n, rem) {
            // SAFETY: we have checked that length of `input` is smaller than
            // number of remaining bytes in `buffer`, so we can safely write data
            // into them and update cursor position.
            unsafe {
                let buf_ptr = self.buffer.as_mut_ptr().cast::<u8>().add(pos);
                ptr::copy_nonoverlapping(input.as_ptr(), buf_ptr, input.len());
                self.set_pos_unchecked(pos + input.len());
            }
            return;
        }
        if pos != 0 {
            let (left, right) = input.split_at(rem);
            input = right;
            // SAFETY: length of `left` is equal to number of remaining bytes in `buffer`,
            // so we can copy data into it and process `buffer` as fully initialized block.
            let block = unsafe {
                let buf_ptr = self.buffer.as_mut_ptr().cast::<u8>().add(pos);
                ptr::copy_nonoverlapping(left.as_ptr(), buf_ptr, left.len());
                self.buffer.assume_init_ref()
            };
            compress(slice::from_ref(block));
        }

        let (blocks, leftover) = K::split_blocks(input);
        if !blocks.is_empty() {
            compress(blocks);
        }

        // SAFETY: `leftover` is always smaller than block size,
        // so it satisfies the method's safety requirements for all buffer kinds
        unsafe {
            self.set_data_unchecked(leftover);
        }
    }

    /// Reset buffer by setting cursor position to zero.
    #[inline(always)]
    pub fn reset(&mut self) {
        // SAFETY: 0 is always valid position
        unsafe {
            self.set_pos_unchecked(0);
        }
    }

    /// Pad remaining data with zeros and return resulting block.
    #[inline(always)]
    pub fn pad_with_zeros(&mut self) -> Array<u8, BS> {
        let mut res = Array::<u8, BS>::default();
        let data = self.get_data();
        res[..data.len()].copy_from_slice(data);
        self.reset();
        res
    }

    /// Return current cursor position.
    #[inline(always)]
    pub fn get_pos(&self) -> usize {
        let pos = K::get_pos(&self.buffer, &self.pos);
        if !K::invariant(pos, BS::USIZE) {
            debug_assert!(false);
            // SAFETY: `pos` never breaks the invariant
            unsafe {
                core::hint::unreachable_unchecked();
            }
        }
        pos
    }

    /// Return slice of data stored inside the buffer.
    #[inline(always)]
    pub fn get_data(&self) -> &[u8] {
        // SAFETY: the `buffer` field is properly initialized up to `self.get_pos()`.
        // `get_pos` never returns position bigger than buffer size.
        unsafe { slice::from_raw_parts(self.buffer.as_ptr().cast(), self.get_pos()) }
    }

    /// Set buffer content and cursor position.
    ///
    /// # Panics
    /// If `pos` is bigger or equal to block size.
    #[inline]
    pub fn set(&mut self, buf: Array<u8, BS>, pos: usize) {
        assert!(K::invariant(pos, BS::USIZE));
        self.buffer = MaybeUninit::new(buf);
        // SAFETY: we have asserted that `pos` satisfies the invariant and
        // the `buffer` field is fully initialized
        unsafe {
            self.set_pos_unchecked(pos);
        }
    }

    /// Return size of the internal buffer in bytes.
    #[inline(always)]
    pub fn size(&self) -> usize {
        BS::USIZE
    }

    /// Return number of remaining bytes in the internal buffer.
    #[inline(always)]
    pub fn remaining(&self) -> usize {
        self.size() - self.get_pos()
    }

    /// Set buffer position.
    ///
    /// # Safety
    /// Bytes in the range of `0..pos` in the `buffer` field must be properly initialized.
    ///
    /// `pos` must satisfy invariant of buffer kind, i.e. for eager hashes it must be
    /// strictly smaller than block size and for lazy hashes it must be smaller or equal
    /// to block size.
    #[inline(always)]
    unsafe fn set_pos_unchecked(&mut self, pos: usize) {
        debug_assert!(K::invariant(pos, BS::USIZE));
        K::set_pos(&mut self.buffer, &mut self.pos, pos)
    }

    /// Set buffer data.
    ///
    /// # Safety
    /// Length of `buf` must satisfy invariant of buffer kind, i.e. for eager hashes it must be
    /// strictly smaller than block size and for lazy hashes it must be smaller or equal
    /// to block size.
    #[inline(always)]
    unsafe fn set_data_unchecked(&mut self, buf: &[u8]) {
        self.set_pos_unchecked(buf.len());
        let dst_ptr: *mut u8 = self.buffer.as_mut_ptr().cast();
        ptr::copy_nonoverlapping(buf.as_ptr(), dst_ptr, buf.len());
    }
}

impl<BS: ArraySize> BlockBuffer<BS, Eager> {
    /// Compress remaining data after padding it with `delim`, zeros and
    /// the `suffix` bytes. If there is not enough unused space, `compress`
    /// will be called twice.
    ///
    /// # Panics
    /// If suffix length is bigger than block size.
    #[inline(always)]
    pub fn digest_pad(
        &mut self,
        delim: u8,
        suffix: &[u8],
        mut compress: impl FnMut(&Array<u8, BS>),
    ) {
        if suffix.len() > BS::USIZE {
            panic!("suffix is too long");
        }
        let pos = self.get_pos();
        let mut buf = self.pad_with_zeros();
        buf[pos] = delim;

        let n = self.size() - suffix.len();
        if self.size() - pos - 1 < suffix.len() {
            compress(&buf);
            buf.fill(0);
            buf[n..].copy_from_slice(suffix);
            compress(&buf);
        } else {
            buf[n..].copy_from_slice(suffix);
            compress(&buf);
        }
        self.reset();
    }

    /// Pad message with 0x80, zeros and 64-bit message length using
    /// big-endian byte order.
    #[inline]
    pub fn len64_padding_be(&mut self, data_len: u64, compress: impl FnMut(&Array<u8, BS>)) {
        self.digest_pad(0x80, &data_len.to_be_bytes(), compress);
    }

    /// Pad message with 0x80, zeros and 64-bit message length using
    /// little-endian byte order.
    #[inline]
    pub fn len64_padding_le(&mut self, data_len: u64, compress: impl FnMut(&Array<u8, BS>)) {
        self.digest_pad(0x80, &data_len.to_le_bytes(), compress);
    }

    /// Pad message with 0x80, zeros and 128-bit message length using
    /// big-endian byte order.
    #[inline]
    pub fn len128_padding_be(&mut self, data_len: u128, compress: impl FnMut(&Array<u8, BS>)) {
        self.digest_pad(0x80, &data_len.to_be_bytes(), compress);
    }

    /// Serialize buffer into a byte array.
    #[inline]
    pub fn serialize(&self) -> Array<u8, BS> {
        let mut res = Array::<u8, BS>::default();
        let data = self.get_data();
        res[..data.len()].copy_from_slice(data);
        res[BS::USIZE - 1] = data.len() as u8;
        res
    }

    /// Deserialize buffer from a byte array.
    #[inline]
    pub fn deserialize(buffer: &Array<u8, BS>) -> Result<Self, Error> {
        let pos = buffer[BS::USIZE - 1] as usize;
        if !<Eager as sealed::Sealed>::invariant(pos, BS::USIZE) {
            return Err(Error);
        }
        if buffer[pos..BS::USIZE - 1].iter().any(|&b| b != 0) {
            return Err(Error);
        }
        Ok(Self {
            buffer: MaybeUninit::new(buffer.clone()),
            pos: Default::default(),
        })
    }
}

impl<BS: ArraySize> BlockBuffer<BS, Lazy> {
    /// Serialize buffer into a byte array.
    #[inline]
    pub fn serialize(&self) -> Array<u8, Add1<BS>>
    where
        BS: Add<B1>,
        Add1<BS>: ArraySize,
    {
        let mut res = Array::<u8, Add1<BS>>::default();
        res[0] = self.pos;
        let data = self.get_data();
        res[1..][..data.len()].copy_from_slice(data);
        res
    }

    /// Deserialize buffer from a byte array.
    #[inline]
    pub fn deserialize(buffer: &Array<u8, Add1<BS>>) -> Result<Self, Error>
    where
        BS: Add<B1>,
        Add1<BS>: ArraySize,
    {
        let pos = buffer[0];
        if !<Lazy as sealed::Sealed>::invariant(pos as usize, BS::USIZE) {
            return Err(Error);
        }
        if buffer[1..][pos as usize..].iter().any(|&b| b != 0) {
            return Err(Error);
        }
        let buf = Array::try_from(&buffer[1..]).expect("slice has correct length");
        Ok(Self {
            buffer: MaybeUninit::new(buf),
            pos,
        })
    }
}

#[cfg(feature = "zeroize")]
impl<BS: ArraySize, K: BufferKind> Zeroize for BlockBuffer<BS, K> {
    #[inline]
    fn zeroize(&mut self) {
        self.buffer.zeroize();
        self.pos.zeroize();
    }
}