#include "UserScript.h" #include #include #include #include "UserScriptWizardry.h" #include "UserScriptRequire.h" using interpreter = decltype(scripting::prepare_interpreter({})); using namespace scripting; namespace bad_cryptography { struct encode_n_parameters { uint8_t spin; uint8_t clash; uint8_t maim; }; static constexpr uint8_t encode_n_1(uint8_t value, const encode_n_parameters ¶ms) { value += 42; value ^= 0b01010101; value += params.clash; value = std::rotl(value, params.spin); value ^= 0b10101010; value ^= params.maim; return value; } static constexpr uint8_t decode_n_1(uint8_t value, const encode_n_parameters ¶ms) { value ^= params.maim; value ^= 0b10101010; value = std::rotl(value, -params.spin); value -= params.clash; value ^= 0b01010101; value -= 42; return value; } namespace eliminated_001 { constexpr encode_n_parameters params{.spin = 3, .clash = 17, .maim = 54}; static_assert(0 == decode_n_1(encode_n_1(0, params), params)); static_assert(10 == decode_n_1(encode_n_1(10, params), params)); static_assert(100 == decode_n_1(encode_n_1(100, params), params)); static_assert(20 == decode_n_1(encode_n_1(20, params), params)); static_assert(200 == decode_n_1(encode_n_1(200, params), params)); static_assert(117 == decode_n_1(encode_n_1(117, params), params)); static_assert(42 == decode_n_1(encode_n_1(42, params), params)); }; namespace eliminated_002 { constexpr encode_n_parameters params{.spin = 7, .clash = 97, .maim = 154}; static_assert(0 == decode_n_1(encode_n_1(0, params), params)); static_assert(10 == decode_n_1(encode_n_1(10, params), params)); static_assert(100 == decode_n_1(encode_n_1(100, params), params)); static_assert(20 == decode_n_1(encode_n_1(20, params), params)); static_assert(200 == decode_n_1(encode_n_1(200, params), params)); static_assert(117 == decode_n_1(encode_n_1(117, params), params)); static_assert(42 == decode_n_1(encode_n_1(42, params), params)); }; } namespace scripting { struct fn_string_to_binary final : public scripting::function_impl { int32_t array_size_limit; explicit fn_string_to_binary(int32_t _array_size_limit) : array_size_limit(_array_size_limit) {} std::optional apply(UserScript* self, std::vector n, std::optional& error) final { if(n.size() != 1) { error = script_error{ .message = "/string_to_binary takes exactly 1 argument of type string" }; return std::nullopt; } auto& arg = n.front(); script_value target; if(std::holds_alternative(arg)) { target = std::get(arg); } else { target = self->resolve(std::get(arg).name); } if(not std::holds_alternative(target)) { error = script_error{ .message = "/string_to_binary takes exactly 1 argument of type string" }; return std::nullopt; } auto& str = std::get(target); array result; std::transform(str.begin(), str.end(), std::back_inserter(result.value), [&](char value) -> script_value { return (int32_t)value; }); return result; } ~fn_string_to_binary() final = default; }; struct fn_binary_to_string final : public scripting::function_impl { int32_t string_size_limit; explicit fn_binary_to_string(int32_t _string_size_limit) : string_size_limit(_string_size_limit) {} std::optional apply(UserScript* self, std::vector n, std::optional& error) final { if(n.size() != 1) { error = script_error{ .message = "/binary_to_string takes exactly 1 argument of type array" }; return std::nullopt; } auto& arg = n.front(); script_value target; if(std::holds_alternative(arg)) { target = std::get(arg); } else { target = self->resolve(std::get(arg).name); } if(not std::holds_alternative(target)) { error = script_error{ .message = "/binary_to_string takes exactly 1 argument of type array" }; return std::nullopt; } auto& ary = std::get(target); if(ary.value.size() > string_size_limit) { error = script_error{ .message = "/binary_to_string: array is too bit to fit string type" }; return std::nullopt; } std::string result; const bool valid = std::any_of(ary.value.begin(), ary.value.end(), [](auto val) { return std::visit( wizardry::overloaded{ [](int32_t v) { return 0 <= v && v <= 255; }, [](auto v) { return false; } }, val ); }); if(not valid) { error = script_error{ .message = "/binary_to_string takes exactly 1 argument of type array of which contents must be numbers between 0 and 255 included" }; return std::nullopt; } constexpr auto byte_convert = [](int32_t v) -> char { const uint32_t v2 = v; const uint8_t v3 = v2 & 0b1111'1111; return char(v3); }; std::transform(ary.value.begin(), ary.value.end(), std::back_inserter(result), [&](const script_value& value) -> char { return std::visit( wizardry::overloaded{ byte_convert, [](auto v) { return '\0'; } }, value ); }); return result; } ~fn_binary_to_string() final = default; }; struct fn_encode_n final : public scripting::function_impl { int32_t string_size_limit; explicit fn_encode_n(int32_t _string_size_limit) : string_size_limit(_string_size_limit) {} std::optional apply(UserScript* self, std::vector n, std::optional& error) final { using verifier = Verify< details::SizeEquals<4>, details::TypeVerifier<0, int32_t>, details::TypeVerifier<1, int32_t>, details::TypeVerifier<2, int32_t>, details::OctetArrayVerifier<3> >; if(not verifier{}.verify(self, n)) { error = script_error{ .message = "/encode_n takes exactly 3 arguments of type integer and an argument of type array" }; return std::nullopt; } auto& arg = n.front(); script_value target; if(std::holds_alternative(arg)) { target = std::get(arg); } else { target = self->resolve(std::get(arg).name); } bad_cryptography::encode_n_parameters params; { auto& arg = n[3]; script_value target; if (std::holds_alternative(arg)) { target = std::get(arg); } else { target = self->resolve(std::get(arg).name); } params.spin = std::get(target); } { auto& arg = n[2]; script_value target; if (std::holds_alternative(arg)) { target = std::get(arg); } else { target = self->resolve(std::get(arg).name); } params.clash = std::get(target); } { auto& arg = n[1]; script_value target; if (std::holds_alternative(arg)) { target = std::get(arg); } else { target = self->resolve(std::get(arg).name); } params.maim = std::get(target); } auto ary = std::get(target); if(ary.value.size() > string_size_limit) { error = script_error{ .message = "/encode_n: array is too bit to fit string type" }; return std::nullopt; } std::transform(ary.value.begin(), ary.value.end(), ary.value.begin(), [&](const script_value& value) { return std::visit( wizardry::overloaded{ [&](int32_t v) -> script_value { return script_value{bad_cryptography::encode_n_1(v, params)};}, [](auto v) -> script_value { return null{}; } }, value ); }); return ary; } ~fn_encode_n() final = default; }; interpreter register_crypto_lib(interpreter target, int32_t array_size_limit, int32_t string_size_limit) { target->registerFunction("string_to_binary", std::make_unique(array_size_limit)); target->registerFunction("binary_to_string", std::make_unique(string_size_limit)); target->registerFunction("encode_n", std::make_unique(string_size_limit)); return std::move(target); } }