Files
password_generator/www/index.php
2026-03-24 15:44:50 -07:00

470 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
ob_start(); // start buffering so we can set cookies before any output
// helper for settings
function getSetting(
string $cookieName,
string $getName,
int $default,
?int $min = null,
?int $max = null
): int {
if (isset($_GET[$getName])) {
$value = intval($_GET[$getName]);
if (!is_null($min) && $value < $min) $value = $min;
if (!is_null($max) && $value > $max) $value = $max;
$cookieOptions = [
'expires' => time() + 86400 * 365,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict',
];
setcookie($cookieName, $value, $cookieOptions);
return $value;
}
if (isset($_COOKIE[$cookieName])) {
return intval($_COOKIE[$cookieName]);
}
$cookieOptions = [
'expires' => time() + 86400 * 365,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict',
];
setcookie($cookieName, $default, $cookieOptions);
return $default;
}
// ---- PASS TYPE COOKIE ----
$defaultPassType = 0;
if (isset($_GET['pt'])) {
$passType = intval($_GET['pt']);
setcookie('passtype', $passType, time() + 86400 * 365, '/');
} elseif (isset($_COOKIE['passtype'])) {
$passType = intval($_COOKIE['passtype']);
} else {
setcookie('passtype', $defaultPassType, time() + 86400 * 365, '/');
$passType = $defaultPassType;
}
// ---- CUSTOM PASSWORD SETTINGS ----
$wMin = getSetting('wMin', 'wMinIn', 6, 3, 10);
$wMax = getSetting('wMax', 'wMaxIn', 12, 3, 10);
$wCount = getSetting('wCount', 'wCountIn', 2, 1, 5);
$sChar = getSetting('sChar', 'sCharIn', 2, 0, 4);
$numLen = getSetting('numLen', 'numLenIn', 3, 0, 8);
function curlHelper($url, $APIKey){
// Initialise cURL
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // return the response as a string
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // follow redirects if any
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // timeout after 10 seconds
// Execute the request
$response = curl_exec($ch);
// Handle cURL errors
if ($response === false) {
$error = curl_error($ch);
curl_close($ch);
throw new Exception("cURL error while calling API: {$error}");
}
// Check HTTP status code
$httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpStatus !== 200) {
throw new Exception("API returned HTTP status {$httpStatus} (expected 200).");
}
$decoded = json_decode($response, true);
if (json_last_error() === JSON_ERROR_NONE) {
if (isset($decoded[$APIKey])) {
return $decoded[$APIKey];
}
}
return trim($response);
}
// Password Generator API Function
function getStandardPasswordFromAPI($passType){
$apiUrl = "http://0.0.0.0:8189/get_password";
// Build the query string and full URL
$query = http_build_query(['pwd_index' => $passType]);
$url = rtrim($apiUrl, '?') . '?' . $query;
return curlHelper($url, "password");
}
// Password Generator API Function for Custom Password
function getCustomPasswordFromAPI($passType, $payload){
$url = 'http://0.0.0.0:8189/custom_password';
// Initialise a cURL handle
$ch = curl_init($url);
// Tell cURL we want to send a POST request with a JSON body
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
// Tell cURL what headers to send
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Content-Length: ' . strlen($payload),
]);
// We want the response body back, not the HTTP headers
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Optional: if you need to trust selfsigned certs (rare for production)
// curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
// Execute the request
$response = curl_exec($ch);
// Basic error handling
if ($response === false) {
// Something went wrong with the cURL call
error_log('cURL error: ' . curl_error($ch));
curl_close($ch);
return false;
}
// Get HTTP status code to confirm the request succeeded
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
// Non200 responses are treated as errors
error_log("Password API returned HTTP {$httpCode}");
return false;
}
// Decode the JSON response
$data = json_decode($response, true);
if ($data === null || !isset($data['password'])) {
error_log('Password API returned malformed JSON');
return false;
}
// Return the password string
return $data['password'];
}
// Password Count API Function
function getPasswordCountFromAPI(){
$apiUrl = "http://0.0.0.0:8189/get_count";
// Build the query string and full URL
$url = rtrim($apiUrl, '?') ;
return curlHelper($url, "total_passwords");
}
function passwordTest_strength($passwordTest) {
$strength = 0;
$possible_points = 12;
$length = strlen($passwordTest);
if (detect_any_uppercase($passwordTest)) $strength += 1;
if (detect_any_lowercase($passwordTest)) $strength += 1;
$strength += min(count_numbers($passwordTest), 2);
$strength += min(count_symbols($passwordTest), 2);
if ($length >= 8) {
$strength += 2;
$strength += min(($length - 8) * 0.5, 4);
}
$strength_percent = $strength / (float)$possible_points;
return floor($strength_percent * 10);
}
function detect_any_uppercase($string) {
return strtolower($string) != $string;
}
function detect_any_lowercase($string) {
return strtoupper($string) != $string;
}
function count_numbers($string) {
return preg_match_all('/[0-9]/', $string);
}
function count_symbols($string) {
$regex = '/[' . preg_quote('!@£$%^&*-_+=?') . ']/';
return preg_match_all($regex, $string);
}
function returnActualPassword($passType){
if ($passType == 3){
$payload = json_encode([
'w_min' => getSetting('wMin', 'wMinIn', 6, 3, 10),
'w_max' => getSetting('wMax', 'wMinIn', 6, 3, 10),
'w_count' => getSetting('wCount', 'wCountIn', 2, 1, 5),
's_char' => getSetting('sChar', 'sCharIn', 2, 0, 4),
'num_len' => getSetting('numLen', 'numLenIn', 3, 0, 8),
]);
return getCustomPasswordFromAPI($passType, $payload);
}
else{
return getStandardPasswordFromAPI($passType);
}
}
$final = returnActualPassword($passType);
#$final = getStandardPasswordFromAPI($passType);
$total = getPasswordCountFromAPI();
$rating = passwordTest_strength($final);
// ---- OUTPUT ----
?>
<!DOCTYPE html>
<html>
<head>
<title>Matt-Cloud Password Generator</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="card">
<h2>Matt-Cloud Password Generator</h2>
<p>Hello folks.<br>
This here is a nice little human-readable password generator. <br>
You've got a few different modes and sometimes it is accidentally funny.<br>
<div class="help-link" id="helpToggle" >API</div>
</div>
<div id="helpText" class="card">
<strong>Matt-Cloud Password API</strong><p>
To get passwords, you may:<p>
<code>
curl -s https://<?php echo $_SERVER['SERVER_NAME'] ?>/get_password?pwd_index=N<br>
{<br>
"password": "-`(UncoloredSwiftly2099"<br>
}
</code><p>
Where N is an integer 0,1, or 2 for now.<p>
To get verbose passwords, you may:<p>
<code>
curl -s https://<?php echo $_SERVER['SERVER_NAME'] ?>/verbose_password?pwd_index=N<br>
{<br>
"descriptor": {<br>
"description": "This simple password is in the following format: !Password123 - this pulls from a list of 1291 simple words.",<br>
"name": "Simple Password",<br>
"type": "2"<br>
},<br>
"password": "&Keenness887",<br>
"password count": 9<br>
}
</code><p>
To get custom passwords, you may:<p>
<code>
curl -X POST https://<?php echo $_SERVER['SERVER_NAME'] ?>/custom_password \ <br>
H "Content-Type: application/json" \ <br>
d '{ <br>
"w_min":5, <br>
"w_max":8, <br>
"w_count":3, <br>
"s_char":2, <br>
"num_len":3, <br>
}' <br>
{<br>
"password": "Copier+ViolinBoned632*"<br>
}
</code><p>
To get the API password count (but why tho?), you may:<p>
<code>
curl -s https://<?php echo $_SERVER['SERVER_NAME'] ?>/get_count<br>
{<br>
"total_passwords": 10<br>
}
</code><p>
To view the password descriptor, you may <br>
<code>
curl -s https://<?php echo $_SERVER['SERVER_NAME'] ?>/get_info<br>
</code>
This will return the entire JSON descriptor variable <br>
</div>
<div class="card">
<?php
// only show the form if a password was generated
if ($passType === 0 || $passType === 1 || $passType === 2 || $passType === 3) {
echo '<form action="#" method="GET">';
// Password output
echo '<input type="text" onClick="this.select();" style="font-size:20pt;border:none;" value="' . htmlspecialchars($final) . '" id="myInput"><br><p>';
echo 'Your password strength is: ' . $rating . '<br><p>';
echo '<button onclick="myFunction()">Copy text</button>';
echo '<input type="button" value="Generate Password" onClick="window.location.reload();">';
// Pass type selector
echo '<select name="pt" onchange="this.form.submit()">';
$types = ['Standard' => 0, 'Windows AD' => 1, 'Simple' => 2, 'Custom' => 3];
foreach ($types as $label => $value) {
echo '<option value="' . $value . '"' . ($passType == $value ? ' selected' : '') . '>' . $label . '</option>';
}
echo '</select>';
//////////////////////////////////////////
// Meter Code
echo '<div id=meter>';
for($i=0; $i < 10; $i++) {
echo "<div";
if($rating > $i) {
echo " class=\"rating-{$rating}\"";
}
echo "></div>";
}
echo '</div>';
////////////////////////////////////////////
// Total count
echo '<br><p>There have been ' . $total . ' total passwords generated thus far.</p>';
if($passType === 0 || $passType === 1 || $passType === 2) {
echo '<button id="info_panel" class="collapsible" type="button">';
echo ' Click here for Password Rules';
echo '</button>';
}
// Custom slider UI (only for type 3)
if ($passType == 3) {
echo '</div><div class="card">';
echo 'Hey folks, this thing finally works.<br><p>';
echo '<table width="500"><tr><td width="200">Minimum Word Length:</td>';
echo '<td width="100"><input type="range" name="wMinIn" id="wMinIn" min="3" max="10" value="' . $wMin . '" oninput="updateTextInput1(this.value);"></td>';
echo '<td width="50"><input type="number" name="wMin" id="wMin" min="3" max="10" value="' . $wMin . '" readonly></td></tr>';
echo '<tr><td width="200">Maximum Word Length:</td>';
echo '<td width="100"><input type="range" name="wMaxIn" id="wMaxIn" min="3" max="10" value="' . $wMax . '" oninput="updateTextInput2(this.value);"></td>';
echo '<td width="50"><input type="number" name="wMax" id="wMax" min="3" max="10" value="' . $wMax . '" readonly></td></tr>';
echo '<tr><td width="200">Number of Words:</td>';
echo '<td width="100"><input type="range" name="wCountIn" id="wCountIn" min="1" max="5" value="' . $wCount . '" oninput="updateTextInput3(this.value);"></td>';
echo '<td width="50"><input type="number" name="wCount" id="wCount" min="1" max="5" value="' . $wCount . '" readonly></td></tr>';
echo '<tr><td width="200">Special Characters:</td>';
echo '<td width="100"><input type="range" name="sCharIn" id="sCharIn" min="0" max="4" value="' . $sChar . '" oninput="updateTextInput4(this.value);"></td>';
echo '<td width="50"><input type="number" name="sChar" id="sChar" min="0" max="4" value="' . $sChar . '" readonly></td></tr>';
echo '<tr><td width="200">Number Length:</td>';
echo '<td width="100"><input type="range" name="numLenIn" id="numLenIn" min="0" max="8" value="' . $numLen . '" oninput="updateTextInput5(this.value);"></td>';
echo '<td width="50"><input type="number" name="numLen" id="numLen" min="0" max="8" value="' . $numLen . '" readonly></td></tr>';
echo '</table><br>';
echo '<button type="submit" class="btn-gen">Generate Password</button>';
//echo '<input type="button" value="Generate Password" onClick="window.location.reload()">';
echo '</div>';
}
if ($passType == 0) {
echo '</div><div id="hidden_info" class="content">';
echo '<p>I have a list of about 20k english words. When you go to this page, I select 60 of these words that are less than 13 characters, ';
echo 'and use the one whose index matches the current second. Then, depending on the length of this word, ';
echo 'this may or may not grab a couple more words from the list, and then it will generate a random number.';
echo 'Then, it shuffles a list of special characters and sprinkles a few of those in with the words and numbers. ';
echo 'Then, once all that is generated, it will shuffle all these things it generated and spit them back. ';
echo 'It takes a hash of the generated password and compares it to a list of all hashes generated in the past. ';
echo 'If the newly generated password is unique, it will print it out under here, otherwise it will try again. ';
echo 'If it generates a bad password, just refresh until you get one you like. This site can never generate the same password twice. ';
echo 'Also, this hash is a one way encryption, so the passwords cannot be re-created from the hashes.</p>';
echo '</div>';
}
if ($passType == 1) {
echo '</div><div id="hidden_info" class="content"><p>';
echo 'This password is always in the following format: <br>';
echo '$Word1Word2Number$<br>';
echo 'Where each word is less than 7 characters, the number is <br>';
echo '3 digits, and the $ represents a Special Charater.</p></div><p>';
}
if ($passType == 2) {
echo '</div><div id="hidden_info" class="content"><br>';
echo 'This simple password is in the following format:<br>';
echo '!Password123<p>';
echo 'Also, this draws from a simpler list of about 3k words.<p></div>';
}
echo '</form>';
} else {
// duplicate hash case
echo '<p>OH FUCK DUPLICATE PASSWORD!!!!1!!<br>';
}
?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const panel = document.getElementById('info_panel');
if (panel) {
panel.addEventListener('click', function () {
const help = document.getElementById('hidden_info');
if (help.style.display === 'none' || help.style.display === '') {
help.style.display = 'block';
} else {
help.style.display = 'none';
}
});
}
});
function myFunction() {
const copyText = document.getElementById("myInput");
copyText.select();
document.execCommand("copy");
alert("Copied the text: " + copyText.value);
}
// limit min/max slider relationship
const elX = document.getElementById("wMaxIn");
const elY = document.getElementById("wMinIn");
function limit() {
if (elX && elY) {
if (elY.value > elX.value) {
elY.value = elX.value;
}
document.getElementById("wMin").value = elY.value;
}
}
if (elX && elY) {
elX.onchange = limit;
elY.onchange = limit;
}
// helper to sync range to number input (already done in form via oninput)
// but keep for safety
function updateTextInput1(val) { document.getElementById('wMin').value = val; }
function updateTextInput2(val) { document.getElementById('wMax').value = val; }
function updateTextInput3(val) { document.getElementById('wCount').value = val; }
function updateTextInput4(val) { document.getElementById('sChar').value = val; }
function updateTextInput5(val) { document.getElementById('numLen').value = val; }
//Toggle the help text when the link is clicked
document.getElementById('helpToggle').addEventListener('click', function () {
const help = document.getElementById('helpText');
if (help.style.display === 'none' || help.style.display === '') {
help.style.display = 'block';
} else {
help.style.display = 'none';
}
});
</script>
</body>
</html>
<?php
ob_end_flush();
?>