2 changed files with 1 additions and 155 deletions
@ -1,154 +0,0 @@
@@ -1,154 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Util\Bech32; |
||||
|
||||
use Exception; |
||||
|
||||
class Bech32Decoder |
||||
{ |
||||
// Function to decode Bech32 without character length restriction |
||||
/** |
||||
* @throws Exception |
||||
*/ |
||||
public function decodeBech32(string $bech32): array |
||||
{ |
||||
$charset = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; |
||||
|
||||
// Find the separator (1) |
||||
$pos = strrpos($bech32, '1'); |
||||
if ($pos === false) { |
||||
throw new Exception('Invalid Bech32 string'); |
||||
} |
||||
|
||||
// Extract human-readable part (HRP) |
||||
$hrp = substr($bech32, 0, $pos); |
||||
|
||||
// Extract data part |
||||
$data_part = substr($bech32, $pos + 1); |
||||
$data = []; |
||||
for ($i = 0; $i < strlen($data_part); $i++) { |
||||
$data[] = strpos($charset, $data_part[$i]); |
||||
if ($data[$i] === false) { |
||||
throw new Exception('Invalid character in Bech32 string'); |
||||
} |
||||
} |
||||
|
||||
return [$hrp, $data]; |
||||
} |
||||
|
||||
// Function to convert 5-bit data to 8-bit data |
||||
public function convertBits(array $data, int $fromBits, int $toBits, bool $pad = true): array |
||||
{ |
||||
$acc = 0; |
||||
$bits = 0; |
||||
$ret = []; |
||||
$maxv = (1 << $toBits) - 1; |
||||
$max_acc = (1 << ($fromBits + $toBits - 1)) - 1; |
||||
|
||||
foreach ($data as $value) { |
||||
if ($value < 0 || $value >> $fromBits) { |
||||
throw new Exception('Invalid value in data'); |
||||
} |
||||
$acc = (($acc << $fromBits) | $value) & $max_acc; |
||||
$bits += $fromBits; |
||||
while ($bits >= $toBits) { |
||||
$bits -= $toBits; |
||||
$ret[] = ($acc >> $bits) & $maxv; |
||||
} |
||||
} |
||||
|
||||
if ($pad) { |
||||
if ($bits > 0) { |
||||
$ret[] = ($acc << ($toBits - $bits)) & $maxv; |
||||
} |
||||
} else if ($bits >= $fromBits || (($acc << ($toBits - $bits)) & $maxv)) { |
||||
throw new Exception('Invalid padding'); |
||||
} |
||||
|
||||
return $ret; |
||||
} |
||||
|
||||
// Public method to decode a Nostr Bech32 string and return hex |
||||
public function decodeNostrBech32ToHex(string $bech32): string |
||||
{ |
||||
list($hrp, $data) = $this->decodeBech32($bech32); |
||||
|
||||
// Convert 5-bit data back to 8-bit data |
||||
$decodedData = $this->convertBits($data, 5, 8, false); |
||||
|
||||
// Return the decoded data as a hex string |
||||
return bin2hex(pack('C*', ...$decodedData)); |
||||
} |
||||
|
||||
// Public method to decode a Nostr Bech32 string and return the binary data |
||||
|
||||
/** |
||||
* @throws Exception |
||||
*/ |
||||
public function decodeNostrBech32ToBinary(string $bech32): array |
||||
{ |
||||
list($hrp, $data) = $this->decodeBech32($bech32); |
||||
|
||||
// Convert 5-bit data to 8-bit data |
||||
$decodedData = $this->convertBits($data, 5, 8); |
||||
|
||||
return [$hrp, $decodedData]; |
||||
} |
||||
|
||||
// Public method to parse the binary data into TLV format |
||||
public function parseTLV(array $binaryData): array |
||||
{ |
||||
$parsedTLVs = []; |
||||
$offset = 0; |
||||
|
||||
while ($offset < count($binaryData)) { |
||||
if ($offset + 1 >= count($binaryData)) { |
||||
throw new Exception("Incomplete TLV data"); |
||||
} |
||||
|
||||
// Read the Type (T) and Length (L) |
||||
$type = $binaryData[$offset]; |
||||
$length = $binaryData[$offset + 1]; |
||||
$offset += 2; |
||||
|
||||
// Ensure we have enough data for the value |
||||
if ($offset + $length > count($binaryData)) { |
||||
break; |
||||
} else { |
||||
// Extract the Value (V) |
||||
$value = array_slice($binaryData, $offset, $length); |
||||
} |
||||
|
||||
$offset += $length; |
||||
|
||||
// Add the TLV to the parsed array |
||||
$parsedTLVs[] = [ |
||||
'type' => $type, |
||||
'length' => $length, |
||||
'value' => $value, |
||||
]; |
||||
} |
||||
|
||||
return $parsedTLVs; |
||||
} |
||||
|
||||
// Decode and parse a Bech32 string |
||||
|
||||
/** |
||||
* @throws Exception |
||||
*/ |
||||
public function decodeAndParseNostrBech32(string $bech32): array |
||||
{ |
||||
// Step 1: Decode Bech32 to binary data |
||||
list($hrp, $binaryData) = $this->decodeNostrBech32ToBinary($bech32); |
||||
|
||||
if ($hrp == 'npub') { |
||||
return [$hrp, $binaryData]; |
||||
} |
||||
|
||||
// Step 2: Parse the binary data into TLV format |
||||
$tlvData = $this->parseTLV($binaryData); |
||||
|
||||
return [$hrp, $tlvData]; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue