1use aws_lc_rs::digest::{Algorithm as DigestAlgo, Context as DigestContext};
2use config::{any_err, from_lua_value, get_or_create_sub_module};
3use crc32fast::Hasher;
4use data_encoding::{
5 BASE32, BASE32HEX, BASE32HEX_NOPAD, BASE32_NOPAD, BASE64, BASE64URL, BASE64URL_NOPAD,
6 BASE64_NOPAD, HEXLOWER,
7};
8use data_loader::KeySource;
9use mlua::prelude::LuaUserData;
10use mlua::{Lua, MetaMethod, UserDataFields, UserDataMethods, Value, Variadic};
11
12fn digest_recursive(value: &Value, ctx: &mut DigestContext) -> anyhow::Result<()> {
13 match value {
14 Value::String(s) => {
15 ctx.update(&s.as_bytes());
16 }
17 _ => {
18 let json = serde_json::to_string(value)?;
19 ctx.update(json.as_bytes());
20 }
21 }
22 Ok(())
23}
24
25fn digest_helper(
26 algorithm: &'static DigestAlgo,
27 args: Variadic<Value>,
28) -> anyhow::Result<BinaryResult> {
29 let mut ctx = DigestContext::new(algorithm);
30 for item in args.iter() {
31 digest_recursive(item, &mut ctx)?;
32 }
33 let digest = ctx.finish();
34 Ok(BinaryResult(digest.as_ref().to_vec()))
35}
36
37pub struct BinaryResult(pub Vec<u8>);
38
39impl LuaUserData for BinaryResult {
40 fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
41 fields.add_field_method_get("hex", |_, this| Ok(HEXLOWER.encode(&this.0)));
42
43 fields.add_field_method_get("base32", |_, this| Ok(BASE32.encode(&this.0)));
44 fields.add_field_method_get("base32_nopad", |_, this| Ok(BASE32_NOPAD.encode(&this.0)));
45
46 fields.add_field_method_get("base32hex", |_, this| Ok(BASE32HEX.encode(&this.0)));
47 fields.add_field_method_get("base32hex_nopad", |_, this| {
48 Ok(BASE32HEX_NOPAD.encode(&this.0))
49 });
50
51 fields.add_field_method_get("base64", |_, this| Ok(BASE64.encode(&this.0)));
52 fields.add_field_method_get("base64_nopad", |_, this| Ok(BASE64_NOPAD.encode(&this.0)));
53
54 fields.add_field_method_get("base64url", |_, this| Ok(BASE64URL.encode(&this.0)));
55 fields.add_field_method_get("base64url_nopad", |_, this| {
56 Ok(BASE64URL_NOPAD.encode(&this.0))
57 });
58
59 fields.add_field_method_get("bytes", |lua, this| lua.create_string(&this.0));
60 }
61
62 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
63 methods.add_meta_method(MetaMethod::ToString, |_, this, _: ()| {
64 Ok(HEXLOWER.encode(&this.0))
65 });
66 }
67}
68
69fn hmac_signer(
70 algo: aws_lc_rs::hmac::Algorithm,
71 key: &[u8],
72 msg: &[u8],
73) -> mlua::Result<BinaryResult> {
74 use aws_lc_rs::hmac::Key;
75
76 let key = Key::new(algo, key);
77 let tag = aws_lc_rs::hmac::sign(&key, msg);
78
79 Ok(BinaryResult(tag.as_ref().to_vec()))
80}
81
82pub fn register(lua: &Lua) -> anyhow::Result<()> {
83 let digest_mod = get_or_create_sub_module(lua, "digest")?;
84
85 macro_rules! digest {
86 ($func_name:literal, $algo:ident) => {
87 digest_mod.set(
88 $func_name,
89 lua.create_function(|_, args: Variadic<Value>| {
90 digest_helper(&aws_lc_rs::digest::$algo, args).map_err(any_err)
91 })?,
92 )?;
93 };
94 }
95
96 macro_rules! hmac {
97 ($func_name:literal, $algo:ident) => {
98 digest_mod.set(
99 concat!("hmac_", $func_name),
100 lua.create_async_function(
101 |lua, (key, msg): (mlua::Value, mlua::String)| async move {
102 let key: KeySource = from_lua_value(&lua, key)?;
103 let key_bytes = key.get().await.map_err(any_err)?;
104
105 hmac_signer(aws_lc_rs::hmac::$algo, &key_bytes, &msg.as_bytes())
106 },
107 )?,
108 )?;
109 };
110 }
111
112 digest!("sha1", SHA1_FOR_LEGACY_USE_ONLY);
113 digest!("sha224", SHA224);
114 digest!("sha256", SHA256);
115 digest!("sha384", SHA384);
116 digest!("sha3_256", SHA3_256);
117 digest!("sha3_384", SHA3_384);
118 digest!("sha3_512", SHA3_512);
119 digest!("sha512", SHA512);
120 digest!("sha512_256", SHA512_256);
121
122 hmac!("sha1", HMAC_SHA1_FOR_LEGACY_USE_ONLY);
123 hmac!("sha224", HMAC_SHA224);
124 hmac!("sha256", HMAC_SHA256);
125 hmac!("sha384", HMAC_SHA384);
126 hmac!("sha512", HMAC_SHA512);
127
128 digest_mod.set(
129 "crc32",
130 lua.create_function(|_, args: Variadic<Value>| {
131 let mut hasher = Hasher::new();
132 for item in args.iter() {
133 match item {
134 Value::String(s) => {
135 hasher.update(&s.as_bytes());
136 }
137 _ => {
138 let json = serde_json::to_string(item).map_err(any_err)?;
139 hasher.update(json.as_bytes());
140 }
141 }
142 }
143 let crc = hasher.finalize();
144 Ok(BinaryResult(crc.to_be_bytes().to_vec()))
145 })?,
146 )?;
147 Ok(())
148}