<?php
// priv2pub_form.php
// فرم وب برای تبدیل Private Key (hex) → Public Key (X,Y) روی secp256k1
// نیازمند: PHP + GMP extension (بدون وابستگی خارجی)

// ---------------------- secp256k1 params ----------------------
$p_hex  = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F";
$n_hex  = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141";
$Gx_hex = "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798";
$Gy_hex = "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8";

// ---------------------- sanity checks ----------------------
if (!extension_loaded('gmp')) {
    die("Error: This page requires PHP GMP extension.");
}

// ---------------------- helpers ----------------------
function hex2gmp(string $hex) { return gmp_init($hex, 16); }
function gmp2hex($g, int $padBytes = 0): string {
    $s = gmp_strval($g, 16);
    $s = strtolower($s);
    if ($padBytes > 0) {
        $need = $padBytes * 2;
        if (strlen($s) < $need) $s = str_pad($s, $need, '0', STR_PAD_LEFT);
    }
    return $s;
}
function mod($x, $m) {
    $r = gmp_mod($x, $m);
    if (gmp_cmp($r, 0) < 0) $r = gmp_add($r, $m);
    return $r;
}
function modAdd($a,$b,$m){ return mod(gmp_add($a,$b), $m); }
function modSub($a,$b,$m){ return mod(gmp_sub($a,$b), $m); }
function modMul($a,$b,$m){ return mod(gmp_mul($a,$b), $m); }
function modInv($a,$m){   return gmp_powm($a, gmp_sub($m,2), $m); } // Fermat (p prime)

// point is ['x'=>gmp,'y'=>gmp] or null (infinity)
function isInf($P){ return $P === null; }

function pointDouble($P, $p){
    if (isInf($P)) return null;
    $x1 = $P['x']; $y1 = $P['y'];
    if (gmp_cmp($y1, 0) === 0) return null; // tangent vertical

    // λ = (3*x1^2) / (2*y1) mod p
    $num = modMul(gmp_mul(3, gmp_mul($x1,$x1)), 1, $p); // 3*x^2
    $den = modMul(gmp_mul(2, $y1), 1, $p);             // 2*y
    $lam = modMul($num, modInv($den,$p), $p);

    $x3 = modSub(modMul($lam,$lam,$p), modMul(2,$x1,$p), $p);
    $y3 = modSub(modMul($lam, modSub($x1,$x3,$p), $p), $y1, $p);
    return ['x'=>$x3, 'y'=>$y3];
}

function pointAdd($P,$Q,$p){
    if (isInf($P)) return $Q;
    if (isInf($Q)) return $P;

    $x1=$P['x']; $y1=$P['y']; $x2=$Q['x']; $y2=$Q['y'];

    if (gmp_cmp($x1,$x2) === 0) {
        // P.x == Q.x  → either P == Q (double) or P == -Q (infinity)
        if (gmp_cmp(mod(gmp_add($y1,$y2),$p), 0) === 0) return null; // P + (-P) = O
        return pointDouble($P,$p); // P == Q
    }

    // λ = (y2 - y1) / (x2 - x1) mod p
    $lam = modMul(modSub($y2,$y1,$p), modInv(modSub($x2,$x1,$p),$p), $p);
    $x3  = modSub(modSub(modMul($lam,$lam,$p), $x1,$p), $x2,$p);
    $y3  = modSub(modMul($lam, modSub($x1,$x3,$p), $p), $y1, $p);
    return ['x'=>$x3, 'y'=>$y3];
}

// Left-to-right binary scalar multiplication: R=O; for each bit: R=2R; if bit=1 then R=R+G
function scalarMultiply($k, $G, $p){
    $R = null; // infinity
    $kbin = gmp_strval($k, 2);
    $len = strlen($kbin);
    for ($i=0; $i<$len; $i++){
        if (!isInf($R)) $R = pointDouble($R, $p);
        if ($kbin[$i] === '1') $R = pointAdd($R, $G, $p);
    }
    return $R;
}

function normalize_hex_input(string $s): string {
    $s = trim($s);
    $s = preg_replace('/^0x/i','',$s);
    $s = preg_replace('/\s+/','',$s);
    $s = strtolower($s);
    if ($s === '') return $s;
    if (!preg_match('/^[0-9a-f]+$/', $s)) return '';
    if (strlen($s) < 64) $s = str_pad($s, 64, '0', STR_PAD_LEFT);
    return $s;
}

// ---------------------- constants as GMP ----------------------
$p  = hex2gmp($p_hex);
$n  = hex2gmp($n_hex);
$Gx = hex2gmp($Gx_hex);
$Gy = hex2gmp($Gy_hex);
$G  = ['x'=>$Gx,'y'=>$Gy];

// ---------------------- handle form ----------------------
$errors = [];
$out = null;
$input_raw = $_POST['priv_hex'] ?? '';

if ($_SERVER['REQUEST_METHOD']==='POST'){
    $hex = normalize_hex_input($input_raw);
    if ($hex === '') {
        $errors[] = "ورودی نامعتبر است. فقط 0-9 و a-f مجاز است.";
    } else {
        $d = hex2gmp($hex);

        // check 1 <= d <= n-1
        if (gmp_cmp($d, 1) < 0 || gmp_cmp($d, gmp_sub($n,1)) > 0) {
            $errors[] = "پرایویت‌کی خارج از بازهٔ معتبر (1 .. n-1) است.";
        } else {
            $Q = scalarMultiply($d, $G, $p);
            if (isInf($Q)) {
                $errors[] = "نتیجه نقطهٔ بینهایت شد (غیرمنتظره).";
            } else {
                $X = gmp2hex($Q['x'], 32);
                $Y = gmp2hex($Q['y'], 32);

                // Uncompressed: 04 || X || Y
                $pub_uncompressed = "04".$X.$Y;

                // Compressed: 02/03 || X  (02 if Y even, 03 if Y odd)
                $y_mod2 = gmp_intval(gmp_mod($Q['y'], 2));
                $prefix = ($y_mod2===0) ? "02" : "03";
                $pub_compressed = $prefix.$X;

                $out = [
                    'priv_hex_norm' => $hex,
                    'X' => $X,
                    'Y' => $Y,
                    'pub_uncompressed' => $pub_uncompressed,
                    'pub_compressed'   => $pub_compressed
                ];
            }
        }
    }
}
?>
<!doctype html>
<html lang="fa" dir="rtl">
<head>
<meta charset="utf-8">
<title>Private → Public (secp256k1) | فرم محاسبه کلید عمومی</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body{font-family:Tahoma,Arial;background:#f6f7fb;color:#111;padding:20px}
.container{max-width:980px;margin:0 auto;background:#fff;border-radius:10px;padding:20px;box-shadow:0 8px 24px rgba(0,0,0,.06)}
h1{margin-top:0;font-size:20px}
label{display:block;margin:10px 0 6px}
input[type=text]{width:100%;padding:10px;border:1px solid #dcdcdc;border-radius:8px;font-family:monospace}
.btn{background:#0b79d0;color:#fff;border:none;border-radius:8px;padding:10px 14px;cursor:pointer}
pre{background:#0f1724;color:#d7e9ff;padding:12px;border-radius:8px;overflow:auto}
.warn{background:#fff4e5;border-left:4px solid #ffb020;padding:10px;border-radius:6px;color:#6b4e00}
.err{background:#ffe8e8;border-left:4px solid #e53935;padding:10px;border-radius:6px}
.row{display:flex;gap:12px;flex-wrap:wrap}
.col{flex:1 1 300px}
.small{color:#666;font-size:13px}
.copy{margin-top:6px}
</style>
</head>
<body>
<div class="container">
  <h1>محاسبهٔ کلید عمومی از پرایویت‌کی (secp256k1)</h1>

  <div class="warn small">
    ⚠️ هشدار: این ابزار آموزشی است. هرگز پرایویت‌کی واقعی را در محیط‌های آنلاین وارد نکنید. بهتر است فقط در لوکال و به‌صورت آفلاین استفاده شود.
  </div>

  <?php if ($errors): ?>
    <div class="err">
      <strong>خطا:</strong>
      <ul>
        <?php foreach ($errors as $e): ?><li><?php echo htmlspecialchars($e); ?></li><?php endforeach; ?>
      </ul>
    </div>
  <?php endif; ?>

  <form method="post" autocomplete="off">
    <label>پرایویت‌کی (Hex) — با یا بدون 0x:</label>
    <input type="text" name="priv_hex" value="<?php echo htmlspecialchars($input_raw); ?>" placeholder="مثال: 0x1e99... یا 1e99..." autofocus>
    <div style="margin-top:10px; display:flex; gap:8px; flex-wrap:wrap">
      <button class="btn" type="submit">محاسبه کلید عمومی</button>
      <button class="btn" type="button" onclick="document.querySelector('input[name=priv_hex]').value='0x0000000000000000000000000000000000000000000000000000000000000002';">قرار دادن مثال (d=2)</button>
    </div>
  </form>

  <?php if ($out): ?>
    <hr>
    <h2>نتایج</h2>

    <div class="row">
      <div class="col">
        <label>Private Key (normalized hex):</label>
        <pre id="privHex">0x<?php echo htmlspecialchars($out['priv_hex_norm']); ?></pre>
        <button class="btn copy" onclick="copy('#privHex')">کپی</button>
      </div>
      <div class="col">
        <label>Public Key (Compressed):</label>
        <pre id="pubC">0x<?php echo htmlspecialchars($out['pub_compressed']); ?></pre>
        <button class="btn copy" onclick="copy('#pubC')">کپی</button>
      </div>
    </div>

    <div class="row" style="margin-top:10px">
      <div class="col">
        <label>Public Key (Uncompressed):</label>
        <pre id="pubU">0x<?php echo htmlspecialchars($out['pub_uncompressed']); ?></pre>
        <button class="btn copy" onclick="copy('#pubU')">کپی</button>
      </div>
      <div class="col">
        <label>X (32 bytes hex) &nbsp;|&nbsp; Y (32 bytes hex):</label>
        <pre id="xy">X = 0x<?php echo htmlspecialchars($out['X']); ?> 
Y = 0x<?php echo htmlspecialchars($out['Y']); ?></pre>
        <button class="btn copy" onclick="copy('#xy')">کپی</button>
      </div>
    </div>

    <p class="small" style="margin-top:12px">
      نکته: برای ساخت آدرس اتریوم: پیشوند 0x04 را حذف کنید، روی X||Y تابع Keccak-256 بگیرید و 20 بایت آخر را بردارید. (EIP-55 صرفاً قالب نمایش است.)
    </p>
  <?php endif; ?>

</div>
<script>
function copy(sel){
  const el = document.querySelector(sel);
  if(!el) return;
  const text = el.innerText || el.textContent;
  navigator.clipboard.writeText(text).then(()=>alert('کپی شد')).catch(()=>alert('کپی نشد'));
}
</script>
</body>
</html>
