470 lines
17 KiB
PHP
470 lines
17 KiB
PHP
<?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://172.17.0.1: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://172.17.0.1: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 self‑signed 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) {
|
||
// Non‑200 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://172.17.0.1: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();
|
||
?>
|