<?php
// ec_pubkey_form.php
// Form to convert EC point (X,Y) to compressed & uncompressed public keys (secp256k1)
// Requires: PHP GMP extension

function h($s){ return htmlspecialchars($s ?? '', ENT_QUOTES, 'UTF-8'); }

function toBigInt($n) {
    $s = trim($n);
    if ($s === '') throw new Exception("Value is empty.");
    // Detect hex if starts with 0x or contains a-f
    if (str_starts_with(strtolower($s), '0x')) {
        return gmp_init(substr($s, 2), 16);
    }
    if (preg_match('/^[0-9a-fA-F]+$/', $s) && preg_match('/[a-fA-F]/', $s)) {
        return gmp_init($s, 16);
    }
    // else decimal
    if (!preg_match('/^[0-9]+$/', $s)) {
        throw new Exception("Invalid number format: only digits for decimal, or hex chars (0-9a-f) for hex.");
    }
    return gmp_init($s, 10);
}

function toFixedHex64(gmp $n): string {
    $hex = gmp_strval($n, 16);
    return str_pad(strtolower($hex), 64, '0', STR_PAD_LEFT);
}

function isEven(gmp $n): bool {
    return gmp_intval(gmp_mod($n, 2)) === 0;
}

function buildPublicKeysFromXY(string $X_in, string $Y_in): array {
    $X = toBigInt($X_in);
    $Y = toBigInt($Y_in);

    // 32 bytes each
    $X64 = toFixedHex64($X);
    $Y64 = toFixedHex64($Y);

    // Uncompressed: 04 || X || Y
    $uncompressed = '04' . $X64 . $Y64;

    // Compressed: 02 if Y even, 03 if Y odd
    $prefix = isEven($Y) ? '02' : '03';
    $compressed = $prefix . $X64;

    return [
        'x_hex'        => strtoupper($X64),
        'y_hex'        => strtoupper($Y64),
        'y_parity'     => isEven($Y) ? 'even' : 'odd',
        'compressed'   => strtoupper($compressed),
        'uncompressed' => strtoupper($uncompressed),
    ];
}

$defaults = [
    'x' => '91803293271015291566676651449823901526449494520260014101994486227127646308120',
    'y' => '12214873553996765113977919971987583165203930680106327820463764688268954204603',
];

$result = null;
$error  = null;

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $x_in = $_POST['x'] ?? '';
    $y_in = $_POST['y'] ?? '';
    try {
        if (!extension_loaded('gmp')) {
            throw new Exception("PHP GMP extension is not enabled. Please enable/install GMP.");
        }
        $result = buildPublicKeysFromXY($x_in, $y_in);
    } catch (Throwable $e) {
        $error = $e->getMessage();
    }
}
?>
<!doctype html>
<html lang="fa" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>EC XY → Public Key (secp256k1)</title>
<style>
  :root { --bg:#0f172a; --card:#111827; --fg:#e5e7eb; --muted:#9ca3af; --acc:#22c55e; }
  *{box-sizing:border-box}
  body{margin:0; font-family:ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto; background:var(--bg); color:var(--fg);}
  .wrap{max-width:960px; margin:40px auto; padding:16px;}
  .card{background:var(--card); border:1px solid #1f2937; border-radius:16px; padding:24px; box-shadow:0 10px 30px rgba(0,0,0,.25);}
  h1{margin:0 0 8px; font-size:24px;}
  p.sub{color:var(--muted); margin-top:0}
  label{display:block; margin-top:16px; font-weight:600;}
  input[type=text], textarea{
    width:100%; padding:12px 14px; border-radius:12px; border:1px solid #334155;
    background:#0b1220; color:var(--fg); font-family:monospace; font-size:14px;
  }
  .hint{color:var(--muted); font-size:12px; margin-top:6px;}
  .row{display:grid; grid-template-columns:1fr 1fr; gap:16px;}
  .btn{margin-top:18px; background:var(--acc); color:#052e12; font-weight:800; border:none; padding:12px 16px; border-radius:12px; cursor:pointer;}
  .btn:hover{filter:brightness(1.1)}
  .out{margin-top:22px; padding:16px; background:#0b1220; border:1px dashed #334155; border-radius:12px; font-family:monospace; overflow:auto}
  .kvs{display:grid; grid-template-columns:220px 1fr; gap:8px; align-items:center}
  .kv{display:contents}
  .err{background:#3b0d0d; color:#fecaca; border:1px solid #7f1d1d; padding:12px 14px; border-radius:12px; margin-top:14px;}
  .muted{color:var(--muted)}
  .copy{float:left; margin-top:10px; font-size:12px; color:#86efac; cursor:pointer}
</style>
</head>
<body>
  <div class="wrap">
    <div class="card">
      <h1>تبدیل مختصات X,Y به Public Key (secp256k1)</h1>
      <p class="sub">ورودی می‌تواند <strong>دسیمال</strong> یا <strong>هگز</strong> باشد (مانند <code>0x...</code> یا فقط حروف/اعداد هگز). خروجی‌ها به صورت هگز هستند.</p>

      <form method="post" action="">
        <div class="row">
          <div>
            <label for="x">Public X</label>
            <input id="x" name="x" type="text" placeholder="Decimal or Hex"
                   value="<?=h($_POST['x'] ?? $defaults['x'])?>">
            <div class="hint">مثال دسیمال: 9180… | مثال هگز: 0xA1B2… یا A1B2…</div>
          </div>
          <div>
            <label for="y">Public Y</label>
            <input id="y" name="y" type="text" placeholder="Decimal or Hex"
                   value="<?=h($_POST['y'] ?? $defaults['y'])?>">
            <div class="hint">برای کلید فشرده فقط زوج/فرد بودن Y مهم است.</div>
          </div>
        </div>
        <button class="btn" type="submit">تولید کلید عمومی</button>
      </form>

      <?php if ($error): ?>
        <div class="err">خطا: <?=h($error)?></div>
      <?php endif; ?>

      <?php if ($result && !$error): ?>
        <div class="out">
          <div class="kvs">
            <div class="kv"><strong>X (hex 64b):</strong> <div><?=h($result['x_hex'])?></div></div>
            <div class="kv"><strong>Y (hex 64b):</strong> <div><?=h($result['y_hex'])?></div></div>
            <div class="kv"><strong>Y parity:</strong> <div><?=h($result['y_parity'])?></div></div>
            <div class="kv"><strong>Compressed:</strong> <div id="cmp"><?=h($result['compressed'])?></div></div>
            <div class="kv"><strong>Uncompressed:</strong> <div id="unc"><?=h($result['uncompressed'])?></div></div>
          </div>
          <span class="copy" onclick="copyAll()">کپی همه خروجی‌ها</span>
        </div>
        <p class="muted">نکته: این خروجی‌ها کلید عمومی خام هستند. اگر آدرس بیت‌کوین/اتریوم می‌خواهی، بگو تا تبدیل کامل را هم اضافه کنم.</p>
      <?php endif; ?>
    </div>
  </div>

<script>
function copyAll(){
  const cmp = document.getElementById('cmp')?.innerText || '';
  const unc = document.getElementById('unc')?.innerText || '';
  const text = `Compressed: ${cmp}\nUncompressed: ${unc}\n`;
  navigator.clipboard.writeText(text).then(()=>alert('کپی شد!'));
}
</script>
</body>
</html>
