First code commit.

This commit is contained in:
deepend 2024-10-20 09:53:39 -06:00
parent 78b921425d
commit f8f07abc2c
7 changed files with 712 additions and 82 deletions

146
README.md
View File

@ -1,11 +1,135 @@
# the1024.club # the1024.club
Goals: Features:
- offer 1k storage to anyone - offer 1k storage to anyone
- no user accounts or registration - no user accounts or registration
- space is assigned based on pub/priv key interactions - space is assigned based on pub/priv key interactions
- api-style endpoints that allocate a space for you based on the hash of your pubkey - api-style endpoints that allocate a space for you based on the hash of your pubkey
- private key sign transactions to the api for CRUD of your 1k - private key sign transactions to the api for CRUD of your 1k
- ability to set mime-type of your 1k - ability to set mime-type of your 1k
This guide will help you set up a 1KB storage site using PHP and SQLite, with public/private key management for storage and retrieval.
## Prerequisites
- PHP 7.4 or higher
- SQLite extension for PHP
- Sodium extension for PHP (for public/private key cryptography)
- Composer (for autoloading)
## Step 1: Clone the Repository
Start by cloning the repository or creating the necessary project structure.
```bash
git clone https://github.com/the1024club/the1024.club.git
cd the1024.club
```
## Step 2: Install Dependencies
# If you don't have Composer installed, first install it
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
php -r "unlink('composer-setup.php');"
Make sure to install the required libraries using Composer.
```bash
php composer.phar require paragonie/sodium_compat
```
## Step 3: Configure the Database
The script will automatically create a SQLite database and table (`storage`) when you first run it. You don't need to manually create the database.
Ensure the database directory is writable:
```bash
mkdir -p /path/to/databasedir
chmod -R 755 /path/to/databasedir
```
## Step 4: Configure PHP Settings
Modify your PHP settings to enable or disable debugging:
- To enable debugging, append `?debug=true` to your URLs.
- To disable debugging, set `debug=false` or omit the `debug` parameter.
## Step 5: Getting Started with using the API
Follow these steps to start using your 1KB block via the API:
### 1. Install Required Dependencies
You need to install the required dependencies via Composer before running the utilities. Run the following commands:
```bash
cd utils
# Install sodium and cURL dependencies
php composer.phar require paragonie/sodium_compat
php composer.phar require guzzlehttp/guzzle
```
### 2. Generate a Key Pair
Use the provided `generate_keys.php` script to generate an Ed25519 public/private key pair:
```bash
# The script will output the Base64-encoded public and private keys, store them in files for use
php generate_keys.php > keys.txt
# You can now copy the public and private keys into separate files:
cat keys.txt | grep 'Public Key' | cut -d ' ' -f3 > public_key.pem
cat keys.txt | grep 'Private Key' | cut -d ' ' -f3 > private_key.pem
```
### 3. Create Your Block
Use the `1kb_client.php` script to create your block on the site:
```bash
# Create a new block using your public key
php 1kb_client.php create public_key.pem
```
### 4. Update Your Block
Sign your data with the private key, then use the `1kb_client.php` script to update your block:
```bash
# Sign your data and update the block with plain text
php 1kb_client.php update public_key.pem private_key.pem "Your data here" "text/plain"
# -or-
# Sign your data and update the block with HTML
php 1kb_client.php update public_key.pem private_key.pem "<h1>Hello, World!</h1>" "text/html"
# -or-
# Sign your data and update the block with an image:
php 1kb_client.php update public_key.pem private_key.pem "$(base64 /path/to/image.png)" "image/png"
```
### 5. Retrieve Your Block
Retrieve the data from your block using the `/retrieve` endpoint:
```bash
# Retrieve data using the public key
php 1kb_client.php retrieve public_key.pem
```
## Step 6: Main Page
By default, `main.php` is loaded when no action is specified. You can customize it with your own content. This file will be displayed to users visiting the root URL of your site.
## Conclusion
Your 1KB storage site is now set up and ready to use. You can interact with it via API calls to store, retrieve, update, and delete small pieces of data. Be sure to manage your public and private keys securely!

5
composer.json Normal file
View File

@ -0,0 +1,5 @@
{
"require": {
"paragonie/sodium_compat": "^2.1"
}
}

View File

@ -1,71 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="shrink-to-fit=no,width=device-width,height=device-height,initial-scale=1,user-scalable=1">
<meta name="description" content="1024 bytes for everyone. What will you do with your 1k?">
<title>The 1024 Club - 1k for Everyone</title>
<link rel="stylesheet" type="text/css" href="/css/main.css" />
</head>
<body>
<header>
<h1>The 1024 Club</h1>
<h2>1k for everybody</h2>
</header>
<nav>
<ul>
<li><a href="/">Home</a></li>
</ul>
</nav>
<main>
<section id="overview">
<h2>Overview</h2>
<p>Everyone deserves a little data to play with. Here at The 1024 Club
we give you a full 1024 bytes to play with as your very own. That's
1k, or about 1/11,796<sup>th</sup> the size of a floppy disk from
1986! What sort of magic can you dream up with yours?</p>
<h3>Features List</h3>
<ul>
<li>no user accounts for registration</li>
<li>api endpoint allocates a block of space for you based on the hash of your pubkey</li>
<li>your private key signs data transactions to the api for CRUD of your 1k block</li>
<li>ability to set mime-type of your block</li>
</ul>
</section>
<section id="getting-started">
<h2>Geting Started</h2>
<p>This section will outline how you can use the API endpoint to
initiate a request for a hashed space. Doing so will initialize your 1k
block. Future interactions with the API will use this hash &amp; your
private key signing data to verify that you have access to manipulate
your block.</p>
</section>
<section id="specification">
<h2>Technical Specifications</h2>
<p>This section will outline the technical stack and various things
about supported key and encryption types.</p>
</section>
</main>
<aside>
<h2>Did you know?</h2>
<p><a href="https://hackaday.io">Hackaday.io</a> ran a contest in 2016
for the best program in under 1k. Why not go
<a href="https://hackaday.io/contest/18215-the-1kb-challenge">see the results of The 1kB Challenge</a>
for yourself.</p>
</aside>
<footer>
<p class="license-title">The MIT License (MIT)</p>
<p class="license-copyright">©2020 The 1024 Club Developers (see AUTHORS.txt)</p>
<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>
<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>
<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
</footer>
</body>
</html>

237
index.php Normal file
View File

@ -0,0 +1,237 @@
<?php
// Load dependencies
require 'vendor/autoload.php';
use ParagonIE\Sodium\CryptoSign;
// Enable or disable debugging/logging
$debug = isset($_GET['debug']) ? (bool)$_GET['debug'] : true;
if ($debug) {
error_reporting(E_ALL);
ini_set('display_errors', 1);
} else {
error_reporting(0);
ini_set('display_errors', 0);
}
// Database setup function
function initializeDatabase($db) {
$query = "
CREATE TABLE IF NOT EXISTS storage (
pubkey_hash CHAR(64) PRIMARY KEY,
data TEXT NOT NULL,
mime_type VARCHAR(50) NOT NULL,
public_key BLOB NOT NULL
)";
$db->exec($query);
// Check if the 'public_key' column already exists; if not, add it.
$columns = $db->query("PRAGMA table_info(storage)")->fetchAll(PDO::FETCH_ASSOC);
$hasPublicKeyColumn = false;
foreach ($columns as $column) {
if ($column['name'] === 'public_key') {
$hasPublicKeyColumn = true;
break;
}
}
if (!$hasPublicKeyColumn) {
$db->exec("ALTER TABLE storage ADD COLUMN public_key BLOB NOT NULL");
}
}
// Initialize SQLite and auto-create the database and table if they don't exist
$db = new PDO('sqlite:/home/retrodig/1024clubdb/storage.db');
initializeDatabase($db);
// Helper function to respond with JSON
function respond($status, $message, $data = []) {
http_response_code($status);
echo json_encode(['message' => $message, 'data' => $data]);
exit;
}
// Enhance Base64 decoding to handle both raw and URL-safe inputs
function safeBase64Decode($input) {
// First, attempt a direct Base64 decode
$decoded = base64_decode($input, true);
if ($decoded !== false && strlen($decoded) === 32) {
return $decoded;
}
// If the direct decode fails, attempt a URL-safe decode
$urlSafeInput = str_replace(['-', '_'], ['+', '/'], $input);
return base64_decode($urlSafeInput, true);
}
// Handle Base64 encoding safely
function safeBase64Encode($input) {
return str_replace(['+', '/'], ['-', '_'], base64_encode($input));
}
// Check if 'action' is set in the query parameters before proceeding
if (isset($_GET['action'])) {
// Create or get storage space based on pubkey hash
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $_GET['action'] === 'create') {
$pubkey = $_POST['pubkey'] ?? null;
if (!$pubkey) {
respond(400, "Missing public key");
}
// Decode the public key, try both raw and URL-safe Base64 decodings
$decodedPubkey = safeBase64Decode($pubkey);
if ($decodedPubkey === false || strlen($decodedPubkey) !== 32) {
respond(400, "Invalid public key");
}
$pubkey_hash = bin2hex(sodium_crypto_generichash($decodedPubkey));
$stmt = $db->prepare("SELECT * FROM storage WHERE pubkey_hash = :pubkey_hash");
$stmt->execute([':pubkey_hash' => $pubkey_hash]);
$existing = $stmt->fetch(PDO::FETCH_ASSOC);
if ($existing) {
respond(200, "Space already exists", ['pubkey_hash' => $pubkey_hash]);
}
$stmt = $db->prepare("INSERT INTO storage (pubkey_hash, data, mime_type, public_key) VALUES (:pubkey_hash, '', 'text/plain', :public_key)");
$stmt->execute([':pubkey_hash' => $pubkey_hash, ':public_key' => $decodedPubkey]);
respond(201, "Space created", ['pubkey_hash' => $pubkey_hash]);
}
// Retrieve stored data using the public key or public key hash
if ($_SERVER['REQUEST_METHOD'] === 'GET' && $_GET['action'] === 'retrieve') {
$pubkey = $_GET['pubkey'] ?? null;
$pubkey_hash = $_GET['pubkey_hash'] ?? null;
if ($pubkey) {
// Replace spaces with '+' to account for unencoded URLs
$pubkey = str_replace(' ', '+', $pubkey);
// Attempt to decode public key (try both standard and URL-safe Base64)
$decodedPubkey = safeBase64Decode($pubkey);
if ($decodedPubkey === false || strlen($decodedPubkey) !== 32) {
respond(400, "Failed to decode public key: " . htmlspecialchars($pubkey));
}
// Hash the decoded public key
$pubkey_hash = bin2hex(sodium_crypto_generichash($decodedPubkey));
} elseif ($pubkey_hash) {
// Validate the provided public key hash (must be 64 characters long, hex format)
if (!ctype_xdigit($pubkey_hash) || strlen($pubkey_hash) !== 64) {
respond(400, "Invalid public key hash: " . htmlspecialchars($pubkey_hash));
}
} else {
respond(400, "Missing public key or public key hash");
}
// Retrieve data by public key hash
$stmt = $db->prepare("SELECT * FROM storage WHERE pubkey_hash = :pubkey_hash");
$stmt->execute([':pubkey_hash' => $pubkey_hash]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result) {
respond(404, "No data found for this public key");
}
// Set the correct MIME type and output the data without encoding
header('Content-Type: ' . $result['mime_type']);
echo $result['data'];
exit;
}
// Update stored data with a signed transaction
if ($_SERVER['REQUEST_METHOD'] === 'PUT' && $_GET['action'] === 'update') {
parse_str(file_get_contents("php://input"), $_PUT);
$pubkey = $_PUT['pubkey'] ?? null;
$signature = $_PUT['signature'] ?? null;
$data = $_PUT['data'] ?? null;
$mime_type = $_PUT['mime_type'] ?? 'text/plain';
if (!$pubkey || !$signature || !$data) {
respond(400, "Invalid input: missing public key, signature, or data");
}
// Decode base64-encoded public key and signature using safe base64 decoding
$decodedPubkey = safeBase64Decode($pubkey);
$decodedSignature = safeBase64Decode($signature);
if ($decodedPubkey === false || strlen($decodedPubkey) !== 32) {
respond(400, "Invalid public key");
}
if ($decodedSignature === false) {
respond(400, "Invalid signature: not valid base64");
}
$pubkey_hash = bin2hex(sodium_crypto_generichash($decodedPubkey));
if (!sodium_crypto_sign_verify_detached($decodedSignature, $data, $decodedPubkey)) {
respond(400, "Invalid signature");
}
$stmt = $db->prepare("SELECT * FROM storage WHERE pubkey_hash = :pubkey_hash");
$stmt->execute([':pubkey_hash' => $pubkey_hash]);
if (!$stmt->fetch(PDO::FETCH_ASSOC)) {
respond(404, "Space not found");
}
if (strlen($data) > 1024) {
respond(400, "Data exceeds 1k size limit");
}
$stmt = $db->prepare("UPDATE storage SET data = :data, mime_type = :mime_type WHERE pubkey_hash = :pubkey_hash");
$stmt->execute([':data' => $data, ':mime_type' => $mime_type, ':pubkey_hash' => $pubkey_hash]);
respond(200, "Storage updated");
}
// Handle deletion (not in spec, but helpful)
if ($_SERVER['REQUEST_METHOD'] === 'DELETE' && $_GET['action'] === 'delete') {
$pubkey = $_POST['pubkey'] ?? null;
$signature = $_POST['signature'] ?? null;
if (!$pubkey || !$signature) {
respond(400, "Invalid input: missing public key or signature");
}
// Decode base64-encoded public key and signature using safe base64 decoding
$decodedPubkey = safeBase64Decode($pubkey);
$decodedSignature = safeBase64Decode($signature);
if ($decodedPubkey === false || strlen($decodedPubkey) !== 32) {
respond(400, "Invalid public key");
}
if ($decodedSignature === false) {
respond(400, "Invalid signature: not valid base64");
}
$pubkey_hash = bin2hex(sodium_crypto_generichash($decodedPubkey));
if (!CryptoSign::verify_detached($decodedSignature, $pubkey_hash, $decodedPubkey)) {
respond(400, "Invalid signature");
}
$stmt = $db->prepare("DELETE FROM storage WHERE pubkey_hash = :pubkey_hash");
$stmt->execute([':pubkey_hash' => $pubkey_hash]);
respond(200, "Storage deleted");
}
} else {
// If no action is specified, show the contents of main.php
if (file_exists('main.php')) {
include 'main.php';
} else {
respond(400, "No action specified and main.php not found");
}
}
?>

177
main.php Normal file
View File

@ -0,0 +1,177 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="shrink-to-fit=no,width=device-width,height=device-height,initial-scale=1,user-scalable=1">
<meta name="description" content="1024 bytes for everyone. What will you do with your 1k?">
<title>The 1024 Club - 1k for Everyone</title>
<link rel="stylesheet" type="text/css" href="/css/main.css" />
</head>
<body>
<header>
<h1>The 1024 Club</h1>
<h2>1k for everybody</h2>
</header>
<nav>
<ul>
<li><a href="/">Home</a></li>
</ul>
</nav>
<main>
<section id="overview">
<h2>Overview</h2>
<p>Everyone deserves a little data to play with. Here at The 1024 Club
we give you a full 1024 bytes to play with as your very own. That's
1k, or about 1/11,796<sup>th</sup> the size of a floppy disk from
1986! What sort of magic can you dream up with yours?</p>
<h3>Features List</h3>
<ul>
<li>No user accounts or registration</li>
<li>API endpoint allocates a block of space for you based on the hash of your public key</li>
<li>Your private key signs data transactions to the API for CRUD of your 1k block</li>
<li>Ability to set MIME-type of your block (e.g., text, image, HTML)</li>
</ul>
</section>
<section id="download-utils">
<h2>Download Utilities</h2>
<p>You can download the utility scripts needed to interact with the API:</p>
<pre><code>
wget https://the1024.club/utils.zip
</code></pre>
<h3>Instructions to Unzip</h3>
<p>After downloading the utils, unzip it using the following command:</p>
<pre><code>
unzip utils.zip
</code></pre>
<p>This will extract the necessary utility scripts for interacting with the 1024 Club API.</p>
</section>
<section id="getting-started">
<h2>Getting Started with the API</h2>
<p>Follow these steps to start using your 1KB block via the API:</p>
<ol>
<li>**Install Required Dependencies**:
You need to install the required dependencies via Composer before running the utilities. Run the following commands:
<pre><code>
# If you don't have Composer installed, first install it
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
php -r "unlink('composer-setup.php');"
# Install sodium and cURL dependencies
php composer.phar require paragonie/sodium_compat
php composer.phar require guzzlehttp/guzzle
</code></pre>
</li>
<li>**Generate a Key Pair**:
Use the provided `generate_keys.php` script to generate an Ed25519 public/private key pair:
<pre><code>
# The script will output the Base64-encoded public and private keys, store them in files for use
php generate_keys.php > keys.txt
# You can now copy the public and private keys into separate files:
cat keys.txt | grep 'Public Key' | cut -d ' ' -f3 > public_key.pem
cat keys.txt | grep 'Private Key' | cut -d ' ' -f3 > private_key.pem
</code></pre>
</li>
<li>**Create Your Block**: Use the `1kb_client.php` script to create your block on the site:
<pre><code>
# Create a new block using your public key
php 1kb_client.php create public_key.pem
</code></pre>
</li>
<li>**Update Your Block**: Sign your data with the private key, then use the `1kb_client.php` script to update your block:
<pre><code>
# Sign your data and update the block with plain text
php 1kb_client.php update public_key.pem private_key.pem "Your data here" "text/plain"
-or-
# Sign your data and update the block with html
php 1kb_client.php update public_key.pem private_key.pem "&lt;h1&gt;Hello, World!&lt;/h1&gt;" "text/html"
-or-
# Sign your data and update the block with an image:
php 1kb_client.php update public_key.pem private_key.pem "$(base64 /path/to/image.png)" "image/png"
</code></pre>
</li>
<li>**Retrieve Your Block**: Retrieve the data from your block using the `/retrieve` endpoint:
<pre><code>
# Retrieve data using the public key
php 1kb_client.php retrieve public_key.pem
</code></pre>
</li>
</ol>
<p>For more details on key generation and signing, see our <a href="#specification">Technical Specifications</a> section below.</p>
</section>
<section id="specification">
<h2>Technical Specifications</h2>
<p>This API uses Ed25519 key pairs for signing and verifying data. The API supports both URL-safe Base64 and non-URL-safe Base64-encoded public keys for all operations. Ensure you generate your keys with a supported tool such as OpenSSL or the `generate_keys.php` script.</p>
<p>To sign your data, use your private key to generate a signature over the data, and then Base64-encode the signature for the API. Both the public key and signature must be Base64-encoded.</p>
<p>The API supports various MIME types, including:</p>
<ul>
<li><strong>text/plain</strong> for plain text</li>
<li><strong>text/html</strong> for HTML data</li>
<li><strong>image/png</strong> for PNG images</li>
</ul>
</section>
<section id="recent-users">
<h2>Last 10 Users' Public 1KB Pages</h2>
<ul>
<?php
// Initialize database connection
try {
$db = new PDO('sqlite:/home/retrodig/1024clubdb/storage.db');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (Exception $e) {
echo '<p>Failed to connect to the database: ' . htmlspecialchars($e->getMessage()) . '</p>';
exit;
}
// Fetch last 10 public keys directly from the database
$stmt = $db->prepare("SELECT public_key FROM storage ORDER BY rowid DESC LIMIT 10");
$stmt->execute();
$keys = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($keys as $key) {
// Base64 encode the actual public key (binary)
$encodedPubKey = base64_encode($key['public_key']);
// Show link using the Base64-encoded public key
echo '<li><a href="/?action=retrieve&pubkey=' . rawurlencode($encodedPubKey) . '">Public Key: ' . htmlspecialchars($encodedPubKey) . '</a></li>';
}
?>
</ul>
</section>
</main>
<aside>
<h2>Did you know?</h2>
<p><a href="https://hackaday.io">Hackaday.io</a> ran a contest in 2016
for the best program in under 1k. Why not go
<a href="https://hackaday.io/contest/18215-the-1kb-challenge">see the results of The 1kB Challenge</a>
for yourself.</p>
</aside>
<footer>
<p class="license-title">The MIT License (MIT)</p>
<p class="license-copyright">©2020 The 1024 Club Developers (see AUTHORS.txt)</p>
<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>
<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>
<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
</footer>
</body>
</html>

144
utils/1kb_client.php Normal file
View File

@ -0,0 +1,144 @@
<?php
// Load Sodium library
require 'vendor/autoload.php';
// Define the base URL for the API
$baseUrl = 'https://the1024.club';
// Display usage instructions
function showUsage() {
echo "Usage:\n";
echo " php 1kb_client.php create <public_key_file>\n";
echo " php 1kb_client.php update <public_key_file> <private_key_file> <data_to_sign> <mime_type>\n";
echo " php 1kb_client.php retrieve <public_key_file>\n";
exit(1);
}
// Function to perform safe base64 encoding (URL safe)
function safeBase64Encode($input) {
return str_replace(['+', '/'], ['-', '_'], base64_encode($input));
}
// Function to perform safe base64 decoding (URL safe)
function safeBase64Decode($input) {
return base64_decode(str_replace(['-', '_'], ['+', '/'], $input));
}
// Function to perform cURL requests
function sendCurlRequest($url, $method, $postData = []) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$response = curl_exec($ch);
if (curl_errno($ch)) {
die('Error: ' . curl_error($ch) . "\n");
}
curl_close($ch);
return $response;
}
// Check if sufficient arguments are provided
if ($argc < 3) {
showUsage();
}
// Get the operation from the command line
$operation = $argv[1];
$publicKeyFile = $argv[2];
// Load public key from file
if (!file_exists($publicKeyFile)) {
die("Public key file not found.\n");
}
$publicKey = trim(file_get_contents($publicKeyFile));
$decodedPublicKey = base64_decode($publicKey);
if ($decodedPublicKey === false || strlen($decodedPublicKey) !== 32) {
die("Invalid public key format. It should be 32 bytes after base64 decoding.\n");
}
$base64PublicKey = safeBase64Encode($decodedPublicKey); // URL-safe encoding
// Create a new 1KB page
if ($operation === 'create') {
// Send the create request with the public key included
$url = "$baseUrl?action=create";
$postData = [
'pubkey' => $base64PublicKey,
'public_key' => $publicKey // Send public key separately to store in the new column
];
$response = sendCurlRequest($url, 'POST', $postData);
echo "Server response: $response\n";
}
// Update an existing 1KB page
elseif ($operation === 'update') {
if ($argc < 6) {
showUsage();
}
$privateKeyFile = $argv[3];
$dataToSign = $argv[4];
$mimeType = $argv[5];
// Load private key from file
if (!file_exists($privateKeyFile)) {
die("Private key file not found.\n");
}
$privateKey = trim(file_get_contents($privateKeyFile));
$decodedPrivateKey = base64_decode($privateKey);
if ($decodedPrivateKey === false || strlen($decodedPrivateKey) !== 64) {
die("Invalid private key format. It should be 64 bytes after base64 decoding.\n");
}
// Sign the data using the private key
$signature = sodium_crypto_sign_detached($dataToSign, $decodedPrivateKey);
$base64Signature = safeBase64Encode($signature); // URL-safe encoding
// Send the update request
$url = "$baseUrl?action=update";
$postData = [
'pubkey' => $base64PublicKey,
'signature' => $base64Signature,
'data' => $dataToSign,
'mime_type' => $mimeType
];
$response = sendCurlRequest($url, 'PUT', $postData);
echo "Server response: $response\n";
}
// Retrieve an existing 1KB page
elseif ($operation === 'retrieve') {
// URL encode the Base64-encoded public key for the URL
$encodedPubKey = rawurlencode($base64PublicKey);
// Send the retrieve request
$url = "$baseUrl?action=retrieve&pubkey=$encodedPubKey";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
die('Error: ' . curl_error($ch) . "\n");
}
curl_close($ch);
echo "Server response: $response\n";
}
// Invalid operation
else {
showUsage();
}
?>

14
utils/generate_keys.php Normal file
View File

@ -0,0 +1,14 @@
<?php
require 'vendor/autoload.php';
// Generate a keypair
$keypair = sodium_crypto_sign_keypair();
// Extract the public and private keys
$publicKey = sodium_crypto_sign_publickey($keypair);
$privateKey = sodium_crypto_sign_secretkey($keypair);
// Encode keys to base64 for easy use
echo "Public Key: " . base64_encode($publicKey) . PHP_EOL;
echo "Private Key: " . base64_encode($privateKey) . PHP_EOL;
?>