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