fix login issue*

This commit is contained in:
2026-02-25 12:26:26 -07:00
parent da581f5a20
commit 2c3d0ab580
37 changed files with 4708 additions and 721 deletions

View File

@@ -6,9 +6,12 @@ defmodule Odinsea.Net.Cipher.AESCipher do
Ported from: src/handling/netty/cipher/AESCipher.java
"""
import Bitwise
@block_size 1460
# MapleStory AES key (32 bytes, expanded from the Java version)
# MapleStory AES key (32 bytes = AES-256)
# Must match the Java AES_KEY exactly
@aes_key <<
0x13, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
@@ -21,7 +24,14 @@ defmodule Odinsea.Net.Cipher.AESCipher do
>>
@doc """
Encrypts or decrypts packet data in place using AES-ECB with IV.
Encrypts or decrypts packet data using AES-ECB with IV.
The algorithm (per block of 1456/1460 bytes):
1. Expand the 4-byte IV to 16 bytes by repeating it 4 times
2. Use AES-ECB to encrypt the expanded IV, producing a 16-byte keystream
3. XOR the next 16 bytes of data with the keystream
4. The encrypted IV becomes the new IV for the next 16-byte chunk
5. Repeat until the block is processed
## Parameters
- data: Binary data to encrypt/decrypt
@@ -32,74 +42,52 @@ defmodule Odinsea.Net.Cipher.AESCipher do
"""
@spec crypt(binary(), binary()) :: binary()
def crypt(data, <<_::binary-size(4)>> = iv) when is_binary(data) do
crypt_recursive(data, iv, 0, byte_size(data), @block_size - 4)
data_list = :binary.bin_to_list(data)
result = crypt_blocks(data_list, 0, @block_size - 4, iv)
:binary.list_to_bin(result)
end
# Recursive encryption/decryption function
@spec crypt_recursive(binary(), binary(), non_neg_integer(), non_neg_integer(), non_neg_integer()) :: binary()
defp crypt_recursive(data, _iv, start, remaining, _length) when remaining <= 0 do
# Return the portion of data we've processed
binary_part(data, 0, start)
end
# Process data in blocks (first block: 1456 bytes, subsequent: 1460 bytes)
defp crypt_blocks([], _start, _length, _iv), do: []
defp crypt_recursive(data, iv, start, remaining, length) do
# Multiply the IV by 4
seq_iv = multiply_bytes(iv, byte_size(iv), 4)
# Adjust length if remaining is smaller
defp crypt_blocks(data, start, length, iv) do
remaining = Kernel.length(data)
actual_length = min(remaining, length)
# Extract the portion of data to process
data_bytes = :binary.bin_to_list(data)
# Expand 4-byte IV to 16 bytes (repeat 4 times)
seq_iv = :binary.copy(iv, 4)
seq_iv_list = :binary.bin_to_list(seq_iv)
# Process the data chunk
{new_data_bytes, _final_seq_iv} =
process_chunk(data_bytes, seq_iv, start, start + actual_length, 0)
# Process this block's bytes, re-encrypting keystream every 16 bytes
{block, rest} = Enum.split(data, actual_length)
{processed, _final_iv} = process_block_bytes(block, seq_iv_list, 0)
# Convert back to binary
new_data = :binary.list_to_bin(new_data_bytes)
# Continue with next chunk
new_start = start + actual_length
new_remaining = remaining - actual_length
new_length = @block_size
crypt_recursive(new_data, iv, new_start, new_remaining, new_length)
# Continue with next block using fresh IV expansion
processed ++ crypt_blocks(rest, start + actual_length, @block_size, iv)
end
# Process a single chunk of data
@spec process_chunk(list(byte()), binary(), non_neg_integer(), non_neg_integer(), non_neg_integer()) ::
{list(byte()), binary()}
defp process_chunk(data_bytes, seq_iv, x, end_x, _offset) when x >= end_x do
{data_bytes, seq_iv}
end
# Process bytes within a single block, re-encrypting keystream every 16 bytes
defp process_block_bytes([], iv_list, _offset), do: {[], iv_list}
defp process_chunk(data_bytes, seq_iv, x, end_x, offset) do
# Check if we need to re-encrypt the IV
{new_seq_iv, new_offset} =
if rem(offset, byte_size(seq_iv)) == 0 do
# Encrypt the IV using AES
encrypted_iv = aes_encrypt_block(seq_iv)
{encrypted_iv, 0}
defp process_block_bytes(data, iv_list, offset) do
# Re-encrypt keystream at every 16-byte boundary
iv_list =
if rem(offset, 16) == 0 do
new_iv = aes_encrypt_block(:binary.list_to_bin(iv_list))
:binary.bin_to_list(new_iv)
else
{seq_iv, offset}
iv_list
end
# XOR the data byte with the IV byte
seq_iv_bytes = :binary.bin_to_list(new_seq_iv)
iv_index = rem(new_offset, length(seq_iv_bytes))
iv_byte = Enum.at(seq_iv_bytes, iv_index)
data_byte = Enum.at(data_bytes, x)
xor_byte = Bitwise.bxor(data_byte, iv_byte)
[byte | rest] = data
iv_byte = Enum.at(iv_list, rem(offset, 16))
xored = bxor(byte, iv_byte)
# Update the data
updated_data = List.replace_at(data_bytes, x, xor_byte)
# Continue processing
process_chunk(updated_data, new_seq_iv, x + 1, end_x, new_offset + 1)
{rest_result, final_iv} = process_block_bytes(rest, iv_list, offset + 1)
{[xored | rest_result], final_iv}
end
# Encrypt a single 16-byte block using AES-ECB
# Encrypt a single 16-byte block using AES-256-ECB
@spec aes_encrypt_block(binary()) :: binary()
defp aes_encrypt_block(block) do
# Pad or truncate to 16 bytes for AES
@@ -110,15 +98,9 @@ defmodule Odinsea.Net.Cipher.AESCipher do
size when size > 16 -> binary_part(block, 0, 16)
end
# Perform AES encryption in ECB mode
:crypto.crypto_one_time(:aes_128_ecb, @aes_key, padded_block, true)
end
# Multiply bytes - repeats the first `count` bytes of `input` `mul` times
@spec multiply_bytes(binary(), non_neg_integer(), non_neg_integer()) :: binary()
defp multiply_bytes(input, count, mul) do
# Take first `count` bytes and repeat them `mul` times
chunk = binary_part(input, 0, min(count, byte_size(input)))
:binary.copy(chunk, mul)
# Perform AES encryption in ECB mode (AES-256)
result = :crypto.crypto_one_time(:aes_256_ecb, @aes_key, padded_block, true)
# Take only first 16 bytes (OpenSSL may add PKCS padding)
binary_part(result, 0, 16)
end
end