1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use config::{any_err, get_or_create_sub_module};
use data_encoding::{
    BASE32, BASE32HEX, BASE32HEX_NOPAD, BASE32_NOPAD, BASE64, BASE64URL, BASE64URL_NOPAD,
    BASE64_NOPAD, HEXLOWER,
};
use mlua::prelude::LuaUserData;
use mlua::{Lua, MetaMethod, UserDataFields, UserDataMethods, Value, Variadic};
use ring::digest::*;

fn digest_recursive(value: &Value, ctx: &mut Context) -> anyhow::Result<()> {
    match value {
        Value::String(s) => {
            ctx.update(s.as_bytes());
        }
        _ => {
            let json = serde_json::to_string(value)?;
            ctx.update(json.as_bytes());
        }
    }
    Ok(())
}

fn digest_helper(
    algorithm: &'static Algorithm,
    args: Variadic<Value>,
) -> anyhow::Result<DigestResult> {
    let mut ctx = Context::new(algorithm);
    for item in args.iter() {
        digest_recursive(item, &mut ctx)?;
    }
    let digest = ctx.finish();
    Ok(DigestResult(digest.as_ref().to_vec()))
}

struct DigestResult(Vec<u8>);

impl LuaUserData for DigestResult {
    fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
        fields.add_field_method_get("hex", |_, this| Ok(HEXLOWER.encode(&this.0)));

        fields.add_field_method_get("base32", |_, this| Ok(BASE32.encode(&this.0)));
        fields.add_field_method_get("base32_nopad", |_, this| Ok(BASE32_NOPAD.encode(&this.0)));

        fields.add_field_method_get("base32hex", |_, this| Ok(BASE32HEX.encode(&this.0)));
        fields.add_field_method_get("base32hex_nopad", |_, this| {
            Ok(BASE32HEX_NOPAD.encode(&this.0))
        });

        fields.add_field_method_get("base64", |_, this| Ok(BASE64.encode(&this.0)));
        fields.add_field_method_get("base64_nopad", |_, this| Ok(BASE64_NOPAD.encode(&this.0)));

        fields.add_field_method_get("base64url", |_, this| Ok(BASE64URL.encode(&this.0)));
        fields.add_field_method_get("base64url_nopad", |_, this| {
            Ok(BASE64URL_NOPAD.encode(&this.0))
        });

        fields.add_field_method_get("bytes", |lua, this| lua.create_string(&this.0));
    }

    fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
        methods.add_meta_method(MetaMethod::ToString, |_, this, _: ()| {
            Ok(HEXLOWER.encode(&this.0))
        });
    }
}

pub fn register(lua: &Lua) -> anyhow::Result<()> {
    let digest_mod = get_or_create_sub_module(lua, "digest")?;

    digest_mod.set(
        "sha1",
        lua.create_function(|_, args: Variadic<Value>| {
            digest_helper(&SHA1_FOR_LEGACY_USE_ONLY, args).map_err(any_err)
        })?,
    )?;
    digest_mod.set(
        "sha256",
        lua.create_function(|_, args: Variadic<Value>| {
            digest_helper(&SHA256, args).map_err(any_err)
        })?,
    )?;
    digest_mod.set(
        "sha384",
        lua.create_function(|_, args: Variadic<Value>| {
            digest_helper(&SHA384, args).map_err(any_err)
        })?,
    )?;
    digest_mod.set(
        "sha512",
        lua.create_function(|_, args: Variadic<Value>| {
            digest_helper(&SHA512, args).map_err(any_err)
        })?,
    )?;
    digest_mod.set(
        "sha512_256",
        lua.create_function(|_, args: Variadic<Value>| {
            digest_helper(&SHA512_256, args).map_err(any_err)
        })?,
    )?;

    Ok(())
}