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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Test the software implementation of SHA256 by performing a hash
//! and checking it against the expected hash value. It uses
//! DigestData::add_date and DigestVerify::verify through the
//! Digest trait.

use core::cell::Cell;
use core::cmp;

use crate::sha256::Sha256Software;
use capsules_core::test::capsule_test::{CapsuleTest, CapsuleTestClient};
use kernel::debug;
use kernel::hil::digest;
use kernel::hil::digest::{Digest, DigestData, DigestVerify};
use kernel::utilities::cells::{OptionalCell, TakeCell};
use kernel::utilities::leasable_buffer::SubSlice;
use kernel::utilities::leasable_buffer::SubSliceMut;
use kernel::ErrorCode;

pub struct TestSha256 {
    sha: &'static Sha256Software<'static>,
    data: TakeCell<'static, [u8]>,     // The data to hash
    hash: TakeCell<'static, [u8; 32]>, // The supplied hash
    position: Cell<usize>,             // Keep track of position in data
    correct: Cell<bool>,               // Whether supplied hash is correct
    client: OptionalCell<&'static dyn CapsuleTestClient>,
}

// We add data in chunks of 12 bytes to ensure that the underlying
// buffering mechanism works correctly (it can handle filling blocks
// as well as zeroing out incomplete blocks).
const CHUNK_SIZE: usize = 12;

impl TestSha256 {
    pub fn new(
        sha: &'static Sha256Software<'static>,
        data: &'static mut [u8],
        hash: &'static mut [u8; 32],
        correct: bool,
    ) -> Self {
        TestSha256 {
            sha,
            data: TakeCell::new(data),
            hash: TakeCell::new(hash),
            position: Cell::new(0),
            correct: Cell::new(correct),
            client: OptionalCell::empty(),
        }
    }

    pub fn run(&'static self) {
        self.sha.set_client(self);
        let data = self.data.take().unwrap();
        let chunk_size = cmp::min(CHUNK_SIZE, data.len());
        self.position.set(chunk_size);
        let mut buffer = SubSliceMut::new(data);
        buffer.slice(0..chunk_size);
        let r = self.sha.add_mut_data(buffer);
        if r.is_err() {
            panic!("Sha256Test: failed to add data: {:?}", r);
        }
    }
}

impl digest::ClientData<32> for TestSha256 {
    fn add_data_done(&self, _result: Result<(), ErrorCode>, _data: SubSlice<'static, u8>) {
        unimplemented!()
    }

    fn add_mut_data_done(&self, result: Result<(), ErrorCode>, mut data: SubSliceMut<'static, u8>) {
        if data.len() != 0 {
            let r = self.sha.add_mut_data(data);
            if r.is_err() {
                panic!("Sha256Test: failed to add data: {:?}", r);
            }
        } else {
            data.reset();
            if self.position.get() < data.len() {
                let new_position = cmp::min(data.len(), self.position.get() + CHUNK_SIZE);
                data.slice(self.position.get()..new_position);
                debug!(
                    "Sha256Test: Setting slice to {}..{}",
                    self.position.get(),
                    new_position
                );
                let r = self.sha.add_mut_data(data);
                if r.is_err() {
                    panic!("Sha256Test: failed to add data: {:?}", r);
                }
                self.position.set(new_position);
            } else {
                data.reset();
                self.data.put(Some(data.take()));
                match result {
                    Ok(()) => {
                        let v = self.sha.verify(self.hash.take().unwrap());
                        if v.is_err() {
                            panic!("Sha256Test: failed to verify: {:?}", v);
                        }
                    }
                    Err(e) => {
                        panic!("Sha256Test: adding data failed: {:?}", e);
                    }
                }
            }
        }
    }
}

impl digest::ClientVerify<32> for TestSha256 {
    fn verification_done(&self, result: Result<bool, ErrorCode>, compare: &'static mut [u8; 32]) {
        self.hash.put(Some(compare));
        debug!("Sha256Test: Verification result: {:?}", result);
        match result {
            Ok(success) => {
                if success != self.correct.get() {
                    panic!(
                        "Sha256Test: Verification should have been {}, was {}",
                        self.correct.get(),
                        success
                    );
                } else {
                    self.client.map(|client| {
                        client.done(Ok(()));
                    });
                }
            }
            Err(e) => {
                panic!("Sha256Test: Error in verification: {:?}", e);
            }
        }
    }
}

impl digest::ClientHash<32> for TestSha256 {
    fn hash_done(&self, _result: Result<(), ErrorCode>, _digest: &'static mut [u8; 32]) {}
}

impl CapsuleTest for TestSha256 {
    fn set_client(&self, client: &'static dyn CapsuleTestClient) {
        self.client.set(client);
    }
}