Skip to content

Instantly share code, notes, and snippets.

@jixunmoe
Created October 13, 2024 21:59
Show Gist options
  • Save jixunmoe/bef901c79f8e89357dd1891e41374d11 to your computer and use it in GitHub Desktop.
Save jixunmoe/bef901c79f8e89357dd1891e41374d11 to your computer and use it in GitHub Desktop.
tc-tea php
<?php
// TC-TEA for PHP by Jixun <https://jixun.moe>
// Licensed under MIT license
namespace TC_TEA {
use Random\RandomException;
const ROUNDS = 16;
const DELTA = 0x9e3779b9;
function u32(int $value): int
{
return $value & 0xffff_ffff;
}
function ecb_single_round(int $value, int $sum, int $key1, int $key2): int
{
$left = ($value << 4) + $key1;
$right = ($value >> 5) + $key2;
$mid = ($sum + $value);
return $left ^ $mid ^ $right;
}
function ecb_encrypt(string $block, string $key): string
{
[, $y, $z] = unpack('N2', $block);
[, $k0, $k1, $k2, $k3] = unpack('N4', $key);
$sum = 0;
for ($i = 0; $i < ROUNDS; $i++) {
$sum += DELTA;
$y = u32($y + ecb_single_round($z, $sum, $k0, $k1));
$z = u32($z + ecb_single_round($y, $sum, $k2, $k3));
}
return pack('NN', $y, $z);
}
function ecb_decrypt(string $block, string $key): string
{
[, $y, $z] = unpack('N2', $block);
[, $k0, $k1, $k2, $k3] = unpack('N4', $key);
$sum = (ROUNDS * DELTA);
for ($i = 0; $i < ROUNDS; $i++) {
$z = u32($z - ecb_single_round($y, $sum, $k2, $k3));
$y = u32($y - ecb_single_round($z, $sum, $k0, $k1));
$sum = $sum - DELTA;
}
return pack('NN', $y, $z);
}
const kSaltLen = 2;
const kZeroLen = 7;
const kFixedPaddingLen = 1 + kSaltLen + kZeroLen;
function xor_tea_block(string $s1, string $s2): string
{
assert(strlen($s1) == 8);
assert(strlen($s2) == 8);
$result = '';
for ($i = 0; $i < 8; $i++) {
$result .= $s1[$i] ^ $s2[$i];
}
return $result;
}
/**
* tc_tea encrypt routine
* @throws RandomException failed to generate random salt
*/
function encrypt(string $data, string $key): string
{
$length = strlen($data) + kFixedPaddingLen;
$paddingLength = (8 - ($length % 8)) % 8;
$outputLength = $length + $paddingLength;
$buffer = '';
$headerLength = 1 + $paddingLength + kSaltLen;
$buffer .= chr((random_int(0, 0x1f) << 3) | $paddingLength);
$buffer .= random_bytes($headerLength - 1);
$buffer .= $data;
$buffer .= str_repeat("\0", 7);
$result = '';
$iv1 = str_repeat("\0", 8);
$iv2 = str_repeat("\0", 8);
for ($i = 0; $i < $outputLength; $i += 8) {
$block = substr($buffer, $i, 8);
$block = xor_tea_block($block, $iv1);
$iv2_next = $block;
$block = ecb_encrypt($block, $key);
$block = xor_tea_block($block, $iv2);
$iv1 = $block;
$iv2 = $iv2_next;
$result .= $block;
}
return $result;
}
/**
* @throws \Exception
*/
function decrypt(string $data, string $key): string
{
if (strlen($data) < 10 || strlen($data) % 8 != 0) {
throw new \Exception('Invalid data length');
}
$len = strlen($data);
$result = '';
$iv1 = str_repeat("\0", 8);
$iv2 = str_repeat("\0", 8);
for ($i = 0; $i < $len; $i += 8) {
$block = substr($data, $i, 8);
$iv1_next = $block;
$block = xor_tea_block($block, $iv2);
$block = ecb_decrypt($block, $key);
$iv2 = $block;
$block = xor_tea_block($block, $iv1);
$iv1 = $iv1_next;
$result .= $block;
}
$blanks = substr($result, -7);
$check = 0;
for ($i = 0; $i < 7; $i++) {
$check |= ord($blanks[$i]);
}
if ($check != 0) {
throw new \Exception('Padding error');
}
$padSize = ord($result[0]) & 0x07;
$startLoc = 1 + $padSize + 2;
$endLoc = strlen($result) - 7;
return substr($result, $startLoc, $endLoc - $startLoc);
}
}
<?php
require_once 'tc_tea.php';
$d1 = \TC_TEA\ecb_decrypt("\x56\x27\x6b\xa9\x80\xb9\xec\x16", "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00");
var_dump(bin2hex($d1));
assert($d1 === "\x01\x02\x03\x04\x05\x06\x07\x08");
mt_srand();
for ($i = 0; $i < 1000; $i++) {
$v = \TC_TEA\decrypt(\TC_TEA\encrypt("wowowowow", "12345678ABCDEFGH"), "12345678ABCDEFGH");
assert($v === "wowowowow");
}
$decrypted = \TC_TEA\decrypt(hex2bin("86ebcc7fa8a354f03fa7575e160b2d09c2238ccc13345a9d"), "12345678ABCDEFGH");
var_dump($decrypted);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment