Last active
March 15, 2024 16:37
-
-
Save JamesTheAwesomeDude/97ab8d893d8fbf42b739e40d3350489c to your computer and use it in GitHub Desktop.
[DRAFT] TeamSpeak 3 Hasher in Elixir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Bitwise | |
require Logger | |
# "MEkDAgcAAgEgAh9n5rQmt3zJukGmwwvEVwfbZFS3NJ9oW7hgpJ0Db5ORAh8mOLmbP3+DfEQvEFbOcqymWoF6s9GHVcv8UegJBcWz" 3210790 1959110000000 | |
defmodule TS3H do | |
def cmd_main(argv) do | |
[pk_str, ctr_best, ctr_cur] = | |
case argv do | |
[a, b, c] -> [a, b, c] | |
[a, b] -> [a, b, b] | |
end | |
{ctr_best, ""} = Integer.parse(ctr_best) | |
{ctr_cur, ""} = Integer.parse(ctr_cur) | |
improve_simple(pk_str, ctr_best, ctr_cur) | |
end | |
def improve_simple(pk_str, ctr_best, ctr_cur) do | |
# dumb, singlethreaded improvement. | |
# does not return if "met", only if "exceeded". | |
record = check(pk_str, ctr_best) | |
intervals(ctr_cur + 1) | |
|> Stream.map(fn range -> work_loop(pk_str, range, record) end) | |
|> Enum.find(fn {outcome, ctr, score} -> if outcome === :beat, do: {ctr, score} end) | |
end | |
defp work_loop(pk_str, range, target) do | |
intermed_state = phase1(pk_str) | |
Enum.reduce_while( | |
range, | |
{nil, -1, -1}, | |
fn ctr, _ -> | |
case phase2(intermed_state, ctr) do | |
score when (score < target) -> | |
{:cont, {nil, ctr, score}} | |
score when (score == target) -> | |
{:halt, {:met, ctr, score}} | |
score when (score > target) -> | |
{:halt, {:beat, ctr, score}} | |
end | |
end | |
) | |
end | |
defp intervals(start \\ 0, size \\ 2**24) do | |
Stream.unfold( | |
start..(start+size-1), | |
fn range -> | |
{range, Range.shift(range, size)} | |
end | |
) | |
end | |
def check(pk_str, ctr) do | |
phase1(pk_str) |> phase2(ctr) | |
end | |
defp phase1(pk_str) do | |
:crypto.hash_init(:sha) | |
|> :crypto.hash_update(pk_str) | |
end | |
defp phase2(state, ctr) do | |
:crypto.hash_update(state, Integer.to_string(ctr)) | |
|> :crypto.hash_final() | |
|> ts3_binary_lzcnt() | |
end | |
def ts3_binary_lzcnt(hash) do | |
# Octets are consumed starting from the lowest memory address. | |
# Bits are consumed starting from the least significant bit. | |
# Examples: | |
# <<255, 0, 0, 0>> -> 0 | |
# << 1, 0, 0, 0>> -> 0 | |
# << 0, 1, 0, 0>> -> 8 | |
# << 0, 255, 0, 0>> -> 8 | |
# << 0, 254, 0, 0>> -> 9 | |
:binary.decode_unsigned(hash, :little) | |
|> ctzg(bit_size(hash)) | |
end | |
defp ctzg(i, max \\ nil) when is_integer(i) do | |
# https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fctzg | |
if i === 0, do: max, else: ctz(i, 0) | |
end | |
defp ctz(i, count) do | |
if rem(i, 2) == 1 do | |
count | |
else | |
ctz(i >>> 1, count + 1) | |
end | |
end | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment