opentitanlib/crypto/
sha256.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use serde::{Deserialize, Serialize};
6use sha2::Digest;
7use std::fmt;
8use std::str::FromStr;
9
10use crate::crypto::Error;
11
12#[derive(Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(transparent)]
14pub struct Sha256Digest {
15    #[serde(with = "serde_bytes")]
16    pub digest: [u8; 32],
17}
18
19impl Sha256Digest {
20    pub fn hash(data: impl AsRef<[u8]>) -> Self {
21        let mut hasher = sha2::Sha256::new();
22        hasher.update(data);
23        Self {
24            digest: hasher.finalize().into(),
25        }
26    }
27
28    pub fn to_vec(&self) -> Vec<u8> {
29        self.digest.to_vec()
30    }
31
32    pub fn to_vec_rev(&self) -> Vec<u8> {
33        let mut result = self.to_vec();
34        result.reverse();
35        result
36    }
37}
38
39impl AsRef<[u8]> for Sha256Digest {
40    fn as_ref(&self) -> &[u8] {
41        &self.digest
42    }
43}
44
45impl TryFrom<&[u8]> for Sha256Digest {
46    type Error = Error;
47    fn try_from(data: &[u8]) -> std::result::Result<Self, Self::Error> {
48        Ok(Sha256Digest {
49            digest: data
50                .try_into()
51                .map_err(|_| Error::InvalidHash(hex::encode(data)))?,
52        })
53    }
54}
55
56impl FromStr for Sha256Digest {
57    type Err = anyhow::Error;
58    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
59        let s = s.trim_start_matches("0x");
60        let data = hex::decode(s)?;
61        Ok(Sha256Digest {
62            digest: data.try_into().map_err(|_| Error::InvalidHash(s.into()))?,
63        })
64    }
65}
66
67impl fmt::Display for Sha256Digest {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        write!(f, "{}", hex::encode(self))
70    }
71}
72
73impl fmt::Debug for Sha256Digest {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        f.debug_struct("Sha256Digest")
76            .field("digest", &self.to_string())
77            .finish()
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn test_sha256() {
87        fn check(msg: &str, digest: &str) {
88            assert_eq!(Sha256Digest::hash(msg).to_string(), digest);
89        }
90        // The digests below can be obtained using `echo -n [msg] | shasum -a 256`.
91        // The digest for "abc" is also available at
92        // https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA256.pdf
93        check(
94            "",
95            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
96        );
97        check(
98            "abc",
99            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
100        );
101        check(
102            "1111",
103            "0ffe1abd1a08215353c233d6e009613e95eec4253832a761af28ff37ac5a150c",
104        );
105    }
106}