<?php
// dec_priv2dec_pub.php
// Converts a private key given in DECIMAL (base-10) to public key (X,Y) in DECIMAL
// Requires: PHP with GMP extension

// ------------------ secp256k1 constants (hex) ------------------
$p_hex  = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F";
$n_hex  = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141";
$Gx_hex = "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798";
$Gy_hex = "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8";

// ------------------ sanity: require GMP ------------------
if (!extension_loaded('gmp')) {
    die("Error: GMP extension required. Install/enable ext-gmp in PHP.\n");
}

// ------------------ helpers ------------------
function hex2gmp(string $hex) { return gmp_init($hex, 16); }
function gmp2dec($g) { return gmp_strval($g, 10); }
function gmp2hex($g, $padBytes = 0) {
    $s = gmp_strval($g, 16);
    if ($padBytes > 0) {
        $need = $padBytes * 2;
        if (strlen($s) < $need) $s = str_pad($s, $need, '0', STR_PAD_LEFT);
    }
    return strtolower($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){
    // a^(m-2) mod m (works because p is prime)
    return gmp_powm($a, gmp_sub($m, 2), $m);
}

// ------------------ point operations (affine) ------------------
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;

    // lambda = (3*x1^2) / (2*y1) mod p
    $num = modMul(gmp_mul(3, gmp_mul($x1,$x1)), 1, $p); // 3*x1^2
    $den = modMul(gmp_mul(2, $y1), 1, $p);             // 2*y1
    $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
        if (gmp_cmp(mod(gmp_add($y1,$y2), $p), 0) === 0) {
            return null; // P + (-P) = infinity
        } else {
            return pointDouble($P, $p); // P == Q
        }
    }

    $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 (safe & simple)
function scalarMultiply($k, $G, $p) {
    $R = null; // point at infinity
    $k_bin = gmp_strval($k, 2); // binary string MSB..LSB
    $len = strlen($k_bin);
    for ($i = 0; $i < $len; $i++) {
        if (!isInf($R)) $R = pointDouble($R, $p);
        if ($k_bin[$i] === '1') {
            $R = pointAdd($R, $G, $p);
        }
    }
    return $R;
}

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

// ------------------ Input handling (form + CLI) ------------------
$input_raw = null;
if (PHP_SAPI === 'cli') {
    $input_raw = $argv[1] ?? null;
} else {
    if ($_SERVER['REQUEST_METHOD'] === 'POST') $input_raw = $_POST['priv_dec'] ?? null;
}

if ($input_raw === null) {
    // If run in CLI without args, print usage
    if (PHP_SAPI === 'cli') {
        echo "Usage: php dec_priv2dec_pub.php <private_decimal>\n";
        echo "Or open this file in a browser and submit a decimal private key via the form.\n";
        exit(0);
    }
}

// ------------------ process input ------------------
$errors = [];
$out = null;

if ($input_raw !== null) {
    $priv_dec = trim($input_raw);
    if ($priv_dec === '') {
        $errors[] = "No input provided.";
    } elseif (!preg_match('/^[0-9]+$/', $priv_dec)) {
        $errors[] = "Invalid input: decimal digits only (0-9).";
    } else {
        // convert decimal string to GMP integer
        $d = gmp_init($priv_dec, 10);

        // check range: 1 <= d <= n-1
        if (gmp_cmp($d, 1) < 0 || gmp_cmp($d, gmp_sub($n, 1)) > 0) {
            $errors[] = "Private key out of range. Must satisfy 1 <= d <= n-1 (secp256k1).";
        } else {
            // compute Q = d * G
            $Q = scalarMultiply($d, $G, $p);
            if (isInf($Q)) {
                $errors[] = "Result is point at infinity (unexpected).";
            } else {
                // output decimal X and Y
                $X_dec = gmp2dec($Q['x']);
                $Y_dec = gmp2dec($Q['y']);
                $out = [
                    'priv_dec' => $priv_dec,
                    'X_dec' => $X_dec,
                    'Y_dec' => $Y_dec,
                    'X_hex' => gmp2hex($Q['x'], 32),
                    'Y_hex' => gmp2hex($Q['y'], 32),
                ];
            }
        }
    }
}

// ------------------ Output (HTML form) ------------------
if (PHP_SAPI !== 'cli') {
    ?>
    <!doctype html>
    <html lang="fa" dir="rtl">
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width,initial-scale=1">
      <title>Decimal Private → Decimal Public (secp256k1)</title>
      <style>
        body{font-family:Tahoma,Arial;background:#f7f9fb;color:#111;padding:20px}
        .box{max-width:980px;margin:0 auto;background:#fff;padding:18px;border-radius:8px;box-shadow:0 8px 20px rgba(0,0,0,.06)}
        input[type=text]{width:100%;padding:10px;border:1px solid #ddd;border-radius:6px;font-family:monospace}
        .btn{background:#0b79d0;color:#fff;padding:10px 14px;border-radius:6px;border:none;cursor:pointer}
        pre{background:#0f1724;color:#d7e9ff;padding:12px;border-radius:6px;overflow:auto}
        .warn{background:#fff4e5;border-left:4px solid #ffb020;padding:10px;margin:10px 0;border-radius:4px}
        .err{background:#ffecec;border-left:4px solid #f44336;padding:10px;margin:10px 0;border-radius:4px}
      </style>
    </head>
    <body>
      <div class="box">
        <h2>تبدیل Private Key (دهدهی) → Public Key (X,Y) دهدهی</h2>
        <div class="warn">⚠️ هشدار امنیتی: این ابزار آموزشی است. هرگز پرایویت‌کی واقعی را در سرورهای ناامن وارد نکنید.</div>

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

        <form method="post">
          <label>پرایویت‌کی (Decimal):</label>
          <input type="text" name="priv_dec" placeholder="مثال: 1117653982188..." value="<?php echo htmlspecialchars($input_raw ?? ''); ?>">
          <div style="margin-top:10px">
            <button class="btn" type="submit">محاسبه کن</button>
            <button class="btn" type="button" onclick="document.querySelector('input[name=priv_dec]').value='111765398218866455599161499737994639270090754107683193886635791750315474235441'">قرار دادن مثال</button>
          </div>
        </form>

        <?php if ($out): ?>
          <hr>
          <h3>نتیجه</h3>
          <label>Private (decimal):</label>
          <pre><?php echo htmlspecialchars($out['priv_dec']); ?></pre>

          <label>Public X (decimal):</label>
          <pre id="xdec"><?php echo htmlspecialchars($out['X_dec']); ?></pre>

          <label>Public Y (decimal):</label>
          <pre id="ydec"><?php echo htmlspecialchars($out['Y_dec']); ?></pre>

          <label>Public X (hex 32B):</label>
          <pre>0x<?php echo htmlspecialchars($out['X_hex']); ?></pre>

          <label>Public Y (hex 32B):</label>
          <pre>0x<?php echo htmlspecialchars($out['Y_hex']); ?></pre>
        <?php endif; ?>

      </div>
    </body>
    </html>
    <?php
    exit(0);
}

// ------------------ CLI output ------------------
if ($out) {
    echo "Private (decimal): " . $out['priv_dec'] . PHP_EOL;
    echo "Public X (decimal): " . $out['X_dec'] . PHP_EOL;
    echo "Public Y (decimal): " . $out['Y_dec'] . PHP_EOL;
    echo "Public X (hex): 0x" . $out['X_hex'] . PHP_EOL;
    echo "Public Y (hex): 0x" . $out['Y_hex'] . PHP_EOL;
    exit(0);
} else {
    // if no output (errors should have been printed earlier in web), just exit
    exit(1);
}
