stacks_core/crypto/
sha256.rs

1pub use bdk::bitcoin::secp256k1;
2use serde::{Deserialize, Serialize};
3use sha2::{Digest, Sha256};
4
5use crate::{
6	crypto::{Hasher, Hashing, Hex},
7	StacksError, StacksResult,
8};
9
10pub(crate) const SHA256_LENGTH: usize = 32;
11
12#[derive(
13	Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord,
14)]
15#[serde(try_from = "Hex")]
16#[serde(into = "Hex")]
17/// The Sha256 hashing type
18pub struct Sha256Hashing([u8; SHA256_LENGTH]);
19
20impl Hashing<SHA256_LENGTH> for Sha256Hashing {
21	fn hash(data: &[u8]) -> Self {
22		Self(Sha256::digest(data).into())
23	}
24
25	fn as_bytes(&self) -> &[u8] {
26		&self.0
27	}
28
29	fn from_bytes(bytes: &[u8]) -> StacksResult<Self> {
30		Ok(Self(bytes.try_into()?))
31	}
32}
33
34// From conversion is fallible for this type
35#[allow(clippy::from_over_into)]
36impl Into<Hex> for Sha256Hashing {
37	fn into(self) -> Hex {
38		Hex(hex::encode(self.as_bytes()))
39	}
40}
41
42impl TryFrom<Hex> for Sha256Hashing {
43	type Error = StacksError;
44
45	fn try_from(value: Hex) -> Result<Self, Self::Error> {
46		Self::from_bytes(&hex::decode(value.0)?)
47	}
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
51/// The DoubleSha256 hashing type
52pub struct DoubleSha256Hashing(Sha256Hashing);
53
54impl Hashing<SHA256_LENGTH> for DoubleSha256Hashing {
55	fn hash(data: &[u8]) -> Self {
56		Self(Sha256Hashing::hash(Sha256Hashing::hash(data).as_bytes()))
57	}
58
59	fn as_bytes(&self) -> &[u8] {
60		self.0.as_bytes()
61	}
62
63	fn from_bytes(bytes: &[u8]) -> StacksResult<Self> {
64		Ok(Self(Sha256Hashing::from_bytes(bytes)?))
65	}
66}
67
68// From conversion is fallible for this type
69#[allow(clippy::from_over_into)]
70impl Into<Hex> for DoubleSha256Hashing {
71	fn into(self) -> Hex {
72		Hex(hex::encode(self.as_bytes()))
73	}
74}
75
76impl TryFrom<Hex> for DoubleSha256Hashing {
77	type Error = StacksError;
78
79	fn try_from(value: Hex) -> Result<Self, Self::Error> {
80		Self::from_bytes(&hex::decode(value.0)?)
81	}
82}
83
84/// The Sha256 hasher type
85pub type Sha256Hasher = Hasher<Sha256Hashing, SHA256_LENGTH>;
86/// The DoubleSha256 hasher type
87pub type DoubleSha256Hasher = Hasher<DoubleSha256Hashing, SHA256_LENGTH>;
88
89#[cfg(test)]
90mod tests {
91	use super::*;
92	use crate::uint::Uint256;
93
94	#[test]
95	fn should_sha256_hash_correctly() {
96		let plaintext = "Hello world";
97		let expected_hash_hex =
98			"64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c";
99
100		assert_eq!(
101			hex::encode(Sha256Hasher::hash(plaintext.as_bytes())),
102			expected_hash_hex
103		);
104	}
105
106	#[test]
107	fn should_sha256_checksum_correctly() {
108		let plaintext = "Hello world";
109		let expected_checksum_hex = "64ec88ca";
110
111		assert_eq!(
112			hex::encode(Sha256Hasher::hash(plaintext.as_bytes()).checksum()),
113			expected_checksum_hex
114		);
115	}
116
117	#[test]
118	fn should_double_sha256_hash_correctly() {
119		let plaintext = "Hello world";
120		let expected_hash_hex =
121			"f6dc724d119649460e47ce719139e521e082be8a9755c5bece181de046ee65fe";
122
123		assert_eq!(
124			hex::encode(
125				DoubleSha256Hasher::hash(plaintext.as_bytes()).as_bytes()
126			),
127			expected_hash_hex
128		);
129	}
130
131	#[test]
132	fn should_double_sha256_checksum_correctly() {
133		let plaintext = "Hello world";
134		let expected_checksum_hex = "f6dc724d";
135
136		assert_eq!(
137			hex::encode(
138				DoubleSha256Hasher::hash(plaintext.as_bytes()).checksum()
139			),
140			expected_checksum_hex
141		);
142	}
143
144	#[test]
145	fn should_convert_to_uint_correctly() {
146		let expected_num = Uint256::from(0xDEADBEEFDEADBEEF_u64) << 64
147			| Uint256::from(0x0102030405060708_u64);
148		let num_bytes = hex::decode(
149			"0807060504030201efbeaddeefbeadde00000000000000000000000000000000",
150		)
151		.unwrap();
152
153		let hash = Sha256Hashing(num_bytes.try_into().unwrap());
154
155		assert_eq!(
156			expected_num,
157			Uint256::from_le_bytes(hash.as_bytes()).unwrap()
158		);
159	}
160}