1use config::{any_err, get_or_create_sub_module};
2use crc32fast::Hasher;
3use data_encoding::{
4 BASE32, BASE32HEX, BASE32HEX_NOPAD, BASE32_NOPAD, BASE64, BASE64URL, BASE64URL_NOPAD,
5 BASE64_NOPAD, HEXLOWER,
6};
7use mlua::prelude::LuaUserData;
8use mlua::{Lua, MetaMethod, UserDataFields, UserDataMethods, Value, Variadic};
9use ring::digest::*;
10
11fn digest_recursive(value: &Value, ctx: &mut Context) -> anyhow::Result<()> {
12 match value {
13 Value::String(s) => {
14 ctx.update(&s.as_bytes());
15 }
16 _ => {
17 let json = serde_json::to_string(value)?;
18 ctx.update(json.as_bytes());
19 }
20 }
21 Ok(())
22}
23
24fn digest_helper(
25 algorithm: &'static Algorithm,
26 args: Variadic<Value>,
27) -> anyhow::Result<DigestResult> {
28 let mut ctx = Context::new(algorithm);
29 for item in args.iter() {
30 digest_recursive(item, &mut ctx)?;
31 }
32 let digest = ctx.finish();
33 Ok(DigestResult(digest.as_ref().to_vec()))
34}
35
36struct DigestResult(Vec<u8>);
37
38impl LuaUserData for DigestResult {
39 fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
40 fields.add_field_method_get("hex", |_, this| Ok(HEXLOWER.encode(&this.0)));
41
42 fields.add_field_method_get("base32", |_, this| Ok(BASE32.encode(&this.0)));
43 fields.add_field_method_get("base32_nopad", |_, this| Ok(BASE32_NOPAD.encode(&this.0)));
44
45 fields.add_field_method_get("base32hex", |_, this| Ok(BASE32HEX.encode(&this.0)));
46 fields.add_field_method_get("base32hex_nopad", |_, this| {
47 Ok(BASE32HEX_NOPAD.encode(&this.0))
48 });
49
50 fields.add_field_method_get("base64", |_, this| Ok(BASE64.encode(&this.0)));
51 fields.add_field_method_get("base64_nopad", |_, this| Ok(BASE64_NOPAD.encode(&this.0)));
52
53 fields.add_field_method_get("base64url", |_, this| Ok(BASE64URL.encode(&this.0)));
54 fields.add_field_method_get("base64url_nopad", |_, this| {
55 Ok(BASE64URL_NOPAD.encode(&this.0))
56 });
57
58 fields.add_field_method_get("bytes", |lua, this| lua.create_string(&this.0));
59 }
60
61 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
62 methods.add_meta_method(MetaMethod::ToString, |_, this, _: ()| {
63 Ok(HEXLOWER.encode(&this.0))
64 });
65 }
66}
67
68pub fn register(lua: &Lua) -> anyhow::Result<()> {
69 let digest_mod = get_or_create_sub_module(lua, "digest")?;
70
71 digest_mod.set(
72 "sha1",
73 lua.create_function(|_, args: Variadic<Value>| {
74 digest_helper(&SHA1_FOR_LEGACY_USE_ONLY, args).map_err(any_err)
75 })?,
76 )?;
77 digest_mod.set(
78 "sha256",
79 lua.create_function(|_, args: Variadic<Value>| {
80 digest_helper(&SHA256, args).map_err(any_err)
81 })?,
82 )?;
83 digest_mod.set(
84 "sha384",
85 lua.create_function(|_, args: Variadic<Value>| {
86 digest_helper(&SHA384, args).map_err(any_err)
87 })?,
88 )?;
89 digest_mod.set(
90 "sha512",
91 lua.create_function(|_, args: Variadic<Value>| {
92 digest_helper(&SHA512, args).map_err(any_err)
93 })?,
94 )?;
95 digest_mod.set(
96 "sha512_256",
97 lua.create_function(|_, args: Variadic<Value>| {
98 digest_helper(&SHA512_256, args).map_err(any_err)
99 })?,
100 )?;
101 digest_mod.set(
102 "crc32",
103 lua.create_function(|_, args: Variadic<Value>| {
104 let mut hasher = Hasher::new();
105 for item in args.iter() {
106 match item {
107 Value::String(s) => {
108 hasher.update(&s.as_bytes());
109 }
110 _ => {
111 let json = serde_json::to_string(item).map_err(any_err)?;
112 hasher.update(json.as_bytes());
113 }
114 }
115 }
116 let crc = hasher.finalize();
117 Ok(DigestResult(crc.to_be_bytes().to_vec()))
118 })?,
119 )?;
120 Ok(())
121}