Skip to main content

lpc55_hal/drivers/
sha.rs

1use core::marker::PhantomData;
2
3use crate::traits::aligned::{Aligned, A4};
4use block_buffer::{BlockBuffer, Eager};
5use digest::{FixedOutput, Output, OutputSizeUser};
6
7use crate::{
8    peripherals::hashcrypt::Hashcrypt,
9    traits::{
10        digest::generic_array::{
11            typenum::{U20, U32, U64},
12            GenericArray,
13        },
14        digest::{Update /*, Reset*/},
15    },
16    typestates::init_state::Enabled,
17};
18
19// no associated types on inherent impls
20type BlockSize = U64;
21// type Sha1OutputSize = U20;
22// type Sha256OutputSize = U32;
23
24// intention of this module is to prevent users from implementing KeySize for anything
25// other than the valid sizes.
26mod sealed {
27    use crate::traits::digest::generic_array::ArrayLength;
28    pub trait OutputSize: ArrayLength<u8> {}
29
30    impl OutputSize for super::U20 {}
31    impl OutputSize for super::U32 {}
32}
33
34use sealed::OutputSize;
35
36pub struct Sha<'a, Size: OutputSize> {
37    buffer: Aligned<A4, BlockBuffer<BlockSize, Eager>>,
38    inner: &'a mut Hashcrypt<Enabled>,
39    len: u64,
40    size: PhantomData<Size>,
41}
42
43pub type Sha1<'a> = Sha<'a, U20>;
44pub type Sha256<'a> = Sha<'a, U32>;
45
46impl<'a, Size: OutputSize> Sha<'a, Size> {
47    pub fn new(hashcrypt: &'a mut Hashcrypt<Enabled>) -> Self {
48        let mut sha = Self {
49            buffer: Aligned(Default::default()),
50            inner: hashcrypt,
51            len: 0,
52            size: PhantomData,
53        };
54        sha.reset();
55        sha
56    }
57
58    pub fn into_inner(self) -> &'a mut Hashcrypt<Enabled> {
59        self.inner
60    }
61
62    pub fn reset(&mut self) {
63        self.buffer = Aligned(Default::default());
64        self.len = 0;
65
66        // SDK says:
67        // /* NEW bit must be set before we switch from previous mode otherwise
68        // new mode will not work correctly */
69        self.inner.ctrl.write(|w| w.new_hash().start());
70        match Size::to_usize() {
71            20 => self
72                .inner
73                .ctrl
74                .write(|w| w.new_hash().start().mode().sha1()),
75            32 => self
76                .inner
77                .ctrl
78                .write(|w| w.new_hash().start().mode().sha2_256()),
79            _ => unreachable!(),
80        }
81    }
82}
83
84impl<'a, Size: OutputSize> From<&'a mut Hashcrypt<Enabled>> for Sha<'a, Size> {
85    fn from(hashcrypt: &'a mut Hashcrypt<Enabled>) -> Self {
86        Sha::new(hashcrypt)
87    }
88}
89
90// the `digest` traits
91
92impl<Size: OutputSize> OutputSizeUser for Sha<'_, Size> {
93    type OutputSize = Size;
94}
95
96impl<Size: OutputSize> FixedOutput for Sha<'_, Size> {
97    fn finalize_into(mut self, out: &mut Output<Self>) {
98        self.finish();
99        // cf `hashcrypt_get_data` ~line 315 of `fsl_hashcrypt.c`
100        for i in 0..Size::to_usize() / 4 {
101            out.as_mut_slice()[4 * i..4 * i + 4]
102                .copy_from_slice(&self.inner.raw.digest0[i].read().bits().to_be_bytes());
103        }
104    }
105}
106
107impl<Size: OutputSize> Update for Sha<'_, Size> {
108    fn update(&mut self, data: &[u8]) {
109        self.update(data.as_ref());
110    }
111}
112
113// the actual implementation
114
115impl<Size: OutputSize> Sha<'_, Size> {
116    fn update(&mut self, data: &[u8]) {
117        // Assumes that input.len() can be converted to u64 without overflow
118        self.len += (data.len() as u64) << 3;
119        // need to convince compiler we're using buffer and peripheral
120        // independently, and not doing a double &mut
121        let peripheral = &mut self.inner;
122        self.buffer.digest_blocks(data, |data| {
123            for b in data {
124                Self::process_block(peripheral, b)
125            }
126        });
127    }
128
129    // relevant code is ~line 800 in fsl_hashcrypt.c
130    fn process_block(peripheral: &mut Hashcrypt<Enabled>, input: &GenericArray<u8, BlockSize>) {
131        // input must be word-aligned
132        let input: Aligned<A4, GenericArray<u8, BlockSize>> = Aligned(*input);
133        let addr: u32 = &input[0] as *const _ as _;
134        assert_eq!(addr & 0x3, 0);
135        while peripheral.raw.status.read().waiting().is_not_waiting() {
136            continue;
137        }
138        peripheral.raw.memaddr.write(|w| unsafe { w.bits(addr) });
139        peripheral
140            .raw
141            .memctrl
142            .write(|w| unsafe { w.master().enabled().count().bits(1) });
143    }
144
145    fn finish(&mut self) {
146        let peripheral = &mut self.inner;
147        let l = self.len;
148        self.buffer
149            .len64_padding_be(l, |block| Self::process_block(peripheral, block));
150        while peripheral.raw.status.read().digest().is_not_ready() {
151            continue;
152        }
153    }
154}
155
156impl<Size: OutputSize> digest::Reset for Sha<'_, Size> {
157    fn reset(&mut self) {
158        self.reset();
159    }
160}