1use crate::compiler::function::EnumVariant;
2use crate::compiler::prelude::*;
3use crate::value;
4use sha_2::{Digest, Sha224, Sha256, Sha384, Sha512, Sha512_224, Sha512_256};
5use std::sync::LazyLock;
6
7static DEFAULT_VARIANT: LazyLock<Value> =
8 LazyLock::new(|| Value::Bytes(Bytes::from("SHA-512/256")));
9
10static VARIANT_ENUM: &[EnumVariant] = &[
11 EnumVariant {
12 value: "SHA-224",
13 description: "SHA-224 algorithm",
14 },
15 EnumVariant {
16 value: "SHA-256",
17 description: "SHA-256 algorithm",
18 },
19 EnumVariant {
20 value: "SHA-384",
21 description: "SHA-384 algorithm",
22 },
23 EnumVariant {
24 value: "SHA-512",
25 description: "SHA-512 algorithm",
26 },
27 EnumVariant {
28 value: "SHA-512/224",
29 description: "SHA-512/224 algorithm",
30 },
31 EnumVariant {
32 value: "SHA-512/256",
33 description: "SHA-512/256 algorithm",
34 },
35];
36
37static PARAMETERS: LazyLock<Vec<Parameter>> = LazyLock::new(|| {
38 vec![
39 Parameter::required(
40 "value",
41 kind::BYTES,
42 "The string to calculate the hash for.",
43 ),
44 Parameter::optional(
45 "variant",
46 kind::BYTES,
47 "The variant of the algorithm to use.",
48 )
49 .default(&DEFAULT_VARIANT)
50 .enum_variants(VARIANT_ENUM),
51 ]
52});
53
54fn sha2(value: Value, variant: &Bytes) -> Resolved {
55 let value = value.try_bytes()?;
56 let hash = match variant.as_ref() {
57 b"SHA-224" => encode::<Sha224>(&value),
58 b"SHA-256" => encode::<Sha256>(&value),
59 b"SHA-384" => encode::<Sha384>(&value),
60 b"SHA-512" => encode::<Sha512>(&value),
61 b"SHA-512/224" => encode::<Sha512_224>(&value),
62 b"SHA-512/256" => encode::<Sha512_256>(&value),
63 _ => unreachable!("enum invariant"),
64 };
65 Ok(hash.into())
66}
67
68#[derive(Clone, Copy, Debug)]
69pub struct Sha2;
70
71fn variants() -> Vec<Value> {
72 vec![
73 value!("SHA-224"),
74 value!("SHA-256"),
75 value!("SHA-384"),
76 value!("SHA-512"),
77 value!("SHA-512/224"),
78 value!("SHA-512/256"),
79 ]
80}
81
82impl Function for Sha2 {
83 fn identifier(&self) -> &'static str {
84 "sha2"
85 }
86
87 fn usage(&self) -> &'static str {
88 "Calculates a [SHA-2](https://en.wikipedia.org/wiki/SHA-2) hash of the `value`."
89 }
90
91 fn category(&self) -> &'static str {
92 Category::Cryptography.as_ref()
93 }
94
95 fn return_kind(&self) -> u16 {
96 kind::BYTES
97 }
98
99 fn parameters(&self) -> &'static [Parameter] {
100 PARAMETERS.as_slice()
101 }
102
103 fn examples(&self) -> &'static [Example] {
104 &[
105 example! {
106 title: "Calculate sha2 hash using default variant",
107 source: r#"sha2("foobar")"#,
108 result: Ok("d014c752bc2be868e16330f47e0c316a5967bcbc9c286a457761d7055b9214ce"),
109 },
110 example! {
111 title: "Calculate sha2 hash with SHA-512/224",
112 source: r#"sha2("foo", variant: "SHA-512/224")"#,
113 result: Ok("d68f258d37d670cfc1ec1001a0394784233f88f056994f9a7e5e99be"),
114 },
115 example! {
116 title: "Calculate sha2 hash with SHA-384",
117 source: r#"sha2("foobar", "SHA-384")"#,
118 result: Ok(
119 "3c9c30d9f665e74d515c842960d4a451c83a0125fd3de7392d7b37231af10c72ea58aedfcdf89a5765bf902af93ecf06",
120 ),
121 },
122 ]
123 }
124
125 fn compile(
126 &self,
127 state: &state::TypeState,
128 _ctx: &mut FunctionCompileContext,
129 arguments: ArgumentList,
130 ) -> Compiled {
131 let value = arguments.required("value");
132 let variant = arguments
133 .optional_enum("variant", &variants(), state)?
134 .unwrap_or_else(|| DEFAULT_VARIANT.clone())
135 .try_bytes()
136 .expect("variant not bytes");
137
138 Ok(Sha2Fn { value, variant }.as_expr())
139 }
140}
141
142#[derive(Debug, Clone)]
143struct Sha2Fn {
144 value: Box<dyn Expression>,
145 variant: Bytes,
146}
147
148impl FunctionExpression for Sha2Fn {
149 fn resolve(&self, ctx: &mut Context) -> Resolved {
150 let value = self.value.resolve(ctx)?;
151 let variant = &self.variant;
152
153 sha2(value, variant)
154 }
155
156 fn type_def(&self, _: &state::TypeState) -> TypeDef {
157 TypeDef::bytes().infallible()
158 }
159}
160
161#[inline]
162fn encode<T: Digest>(value: &[u8]) -> String {
163 hex::encode(T::digest(value))
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 test_function![
171 sha2 => Sha2;
172
173 sha2 {
174 args: func_args![value: "foo"],
175 want: Ok("d58042e6aa5a335e03ad576c6a9e43b41591bfd2077f72dec9df7930e492055d"),
176 tdef: TypeDef::bytes().infallible(),
177 }
178
179 sha2_224 {
180 args: func_args![value: "foo",
181 variant: "SHA-224"
182 ],
183 want: Ok("0808f64e60d58979fcb676c96ec938270dea42445aeefcd3a4e6f8db"),
184 tdef: TypeDef::bytes().infallible(),
185 }
186
187 sha2_256 {
188 args: func_args![value: "foo",
189 variant: "SHA-256"
190 ],
191 want: Ok("2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"),
192 tdef: TypeDef::bytes().infallible(),
193 }
194
195 sha2_385 {
196 args: func_args![value: "foo",
197 variant: "SHA-384"
198 ],
199 want: Ok("98c11ffdfdd540676b1a137cb1a22b2a70350c9a44171d6b1180c6be5cbb2ee3f79d532c8a1dd9ef2e8e08e752a3babb"),
200 tdef: TypeDef::bytes().infallible(),
201 }
202
203 sha2_512 {
204 args: func_args![value: "foo",
205 variant: "SHA-512"
206 ],
207 want: Ok("f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7"),
208 tdef: TypeDef::bytes().infallible(),
209 }
210
211 sha2_512_224 {
212 args: func_args![value: "foo",
213 variant: "SHA-512/224"
214 ],
215 want: Ok("d68f258d37d670cfc1ec1001a0394784233f88f056994f9a7e5e99be"),
216 tdef: TypeDef::bytes().infallible(),
217 }
218
219 sha2_512_256 {
220 args: func_args![value: "foo",
221 variant: "SHA-512/256"
222 ],
223 want: Ok("d58042e6aa5a335e03ad576c6a9e43b41591bfd2077f72dec9df7930e492055d"),
224 tdef: TypeDef::bytes().infallible(),
225 }
226 ];
227}