OAuth & SSH Keygen

This commit is contained in:
Elizabeth Cray 2022-05-01 20:20:59 +00:00
parent 155afadba9
commit 60f182b99d
15 changed files with 1494 additions and 5 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
vendor/

View File

@ -1,3 +1,5 @@
# Hackers Town Tilde User Generator
use Mastodon oauth to generate system users
Warrant canary available at /etc/ttyserver/canary

155
auth/index.php Normal file
View File

@ -0,0 +1,155 @@
<?php
$config = json_decode(file_get_contents("/var/www/usergen/config.json", true));
if (isset($_REQUEST["act"])){
// internal functions such as id request
switch($_REQUEST["act"]){
case "id":
// return OAUTH app ID
header('Content-type: application/json');
echo json_encode(array("id" => $config->oauth->key));
exit();
break;
default:
break;
}
}else if (isset($_REQUEST["code"])){
// Mastodon callback
$MastCode = $_REQUEST["code"];
}
?>
<HTML lang="en">
<Head>
<Title>HackersTown Server Access</Title>
<meta charset="utf-8">
<base href="/auth"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Stylesheets -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="/style.css" rel="stylesheet"/>
<!-- Javascript -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/color/jquery.color.plus-names-2.1.2.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/jszip@3.9.1/dist/jszip.min.js" integrity="sha256-aSPPIlJfSHQ5T7wunbPcp7tM0rlq5dHoUGeN8O5odMg=" crossorigin="anonymous"></script>
<script src="/base64url.js"></script>
<script src="/ssh-util.js"></script>
<script src="/keygen.js"></script>
<script src="/fittext.js"></script>
<script src="/index.js"></script>
</Head>
<Body>
<div class="row">
<div class="desktopOnly col-4"></div>
<div id="content" class="col-4 center">
<div class="row">
<a href="https://tty.hackers.town">
<img src="/Assets/HTown.png" class="logo self-align-center mx-auto d-block" alt="Hacker Town logo in ASCII art. Rendered as image to force correct visualization."/>
</a>
</div>
<?php
// Query /oauth/token
$AuthToken = "";
$UserName = "";
$ErrorDesc = "";
$request = curl_init();
curl_setopt($request, CURLOPT_POST, 1);
curl_setopt($request, CURLOPT_URL, "https://hackers.town/oauth/token");
curl_setopt($request, CURLOPT_RETURNTRANSFER, 1);
// $reqirectUri = "urn:ietf:wg:oauth:2.0:oob";
$reqirectUri = "https://tty.hackers.town/auth";
$options = "grant_type=authorization_code&code=".$MastCode."&client_id=".$config->oauth->key."&client_secret=".$config->oauth->secret."&scope=read:accounts&redirect_uri=".$reqirectUri;
curl_setopt($request, CURLOPT_POSTFIELDS, $options);
// echo $options;
// echo json_encode($request);
$response = curl_exec($request);
curl_close($request);
// echo $response;
$Auth = json_decode($response);
if(isset($Auth->token_type)){
// Valid Auth?
$request = curl_init();
curl_setopt($request, CURLOPT_URL, "https://hackers.town/api/v1/accounts/verify_credentials");
curl_setopt($request, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($request, CURLOPT_HTTPHEADER, array(
"Authorization: ".$Auth->token_type." ".$Auth->access_token
));
$response = curl_exec($request);
curl_close($request);
$User = json_decode($response);
if (isset($User->id)){
// Congrats!
$AuthToken = $Auth->access_token;
$UserName = $User->display_name;
}else{
// invalid auth
$AuthToken = "BadUser";
$ErrorDesc = "User Not Found";
}
}else{
// invalid auth
$AuthToken = "BadOauth";
$ErrorDesc = "Invalid OAuth";
}
// revoke token after usage
?>
<div id="usertoken" hidden><?php echo $AuthToken; ?></div>
<div class="row"<?php if(strpos($AuthToken, "Bad") === false){
echo "hidden";
}?>>
<!-- Select to upload public key or generate a new one locally -->
<span>
Invalid
</span>
<span>
try again
</span>
<div class="message">
<?php echo $ErrorDesc; ?>
</div>
</div><div class="row button"<?php if(strpos($AuthToken, "Bad") === false){
echo "hidden";
}?>>
<!-- Select to upload public key or generate a new one locally -->
<button class="col keyButton" onclick="beginOauth()">Retry</button>
</div>
<div class="row"<?php if(strpos($AuthToken, "Bad") !== false){
echo "hidden";
}?>>
<!-- Select to upload public key or generate a new one locally -->
<span>
<?php
$Welcomes = array("Welcome", "Dobrodošli", "Vitejte", "Welkom", "Tervetuloa", "Willkommen", "Fáilte", "Benvenuto", "Bienvenidos", "Välkommen", "ようこそ");
echo $Welcomes[array_rand($Welcomes)];
?>
</span>
<span id="resizer">
<?php echo $UserName; ?>
</span>
<div class="message">
Setup an account SSH key
</div>
</div>
<div class="row button" <?php if(strpos($AuthToken, "Bad") !== false){
echo "hidden";
}?>>
<button class="col keyButton" onclick="generateSSH('<?php echo $UserName; ?>')">Generate</button>
<button class="col keyButton" onclick="uploadSSH()">Upload</button>
</div>
<div class="row copyright">
<!-- TODO: Make this file PHP and make the canary dependent on /etc/ttyserver/canary -->
<?php
if (file_exists("/etc/ttyserver/canary")){
echo "Canary";
}
?>
<br>
Copyright ©️ 2022 Elizabeth Anne Cray
</div>
</div>
<div class="desktopOnly col-4"></div>
</div>
</Body>
</HTML>

31
base64url.js Normal file
View File

@ -0,0 +1,31 @@
// adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-08#appendix-C
function base64urlEncode(arg) {
const step1 = window.btoa(arg); // Regular base64 encoder
const step2 = step1.split("=")[0]; // Remove any trailing '='s
const step3 = step2.replace(/\+/g, "-"); // 62nd char of encoding
const step4 = step3.replace(/\//g, "_"); // 63rd char of encoding
return step4;
}
function base64urlDecode(s) {
const step1 = s.replace(/-/g, "+"); // 62nd char of encoding
const step2 = step1.replace(/_/g, "/"); // 63rd char of encoding
let step3 = step2;
switch (step2.length % 4) { // Pad with trailing '='s
case 0: // No pad chars in this case
break;
case 2: // Two pad chars
step3 += "==";
break;
case 3: // One pad char
step3 += "=";
break;
default:
throw new Error("Illegal base64url string!");
}
return window.atob(step3); // Regular base64 decoder
}
const module = window.module || {};
module.exports = { base64urlDecode, base64urlEncode };

5
composer.json Normal file
View File

@ -0,0 +1,5 @@
{
"require": {
"lrf141/oauth2-mastodon": "^1.0"
}
}

794
composer.lock generated Normal file
View File

@ -0,0 +1,794 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d823e547e45f296dd119344b20f3fbcd",
"packages": [
{
"name": "guzzlehttp/guzzle",
"version": "7.4.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/ac1ec1cd9b5624694c3a40be801d94137afb12b4",
"reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^1.5",
"guzzlehttp/psr7": "^1.8.3 || ^2.1",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
},
"provide": {
"psr/http-client-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.4.1",
"ext-curl": "*",
"php-http/client-integration-tests": "^3.0",
"phpunit/phpunit": "^8.5.5 || ^9.3.5",
"psr/log": "^1.1 || ^2.0 || ^3.0"
},
"suggest": {
"ext-curl": "Required for CURL handler support",
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
"psr/log": "Required for using the Log middleware"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "7.4-dev"
}
},
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Jeremy Lindblom",
"email": "jeremeamia@gmail.com",
"homepage": "https://github.com/jeremeamia"
},
{
"name": "George Mponos",
"email": "gmponos@gmail.com",
"homepage": "https://github.com/gmponos"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://github.com/sagikazarmark"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
}
],
"description": "Guzzle is a PHP HTTP client library",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"psr-18",
"psr-7",
"rest",
"web service"
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.4.2"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
"type": "tidelift"
}
],
"time": "2022-03-20T14:16:28+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
"shasum": ""
},
"require": {
"php": ">=5.5"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.4 || ^5.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.5-dev"
}
},
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
}
],
"description": "Guzzle promises library",
"keywords": [
"promise"
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/1.5.1"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
"type": "tidelift"
}
],
"time": "2021-10-22T20:56:57+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "2.2.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "c94a94f120803a18554c1805ef2e539f8285f9a2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/c94a94f120803a18554c1805ef2e539f8285f9a2",
"reference": "c94a94f120803a18554c1805ef2e539f8285f9a2",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0",
"ralouphie/getallheaders": "^3.0"
},
"provide": {
"psr/http-factory-implementation": "1.0",
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.4.1",
"http-interop/http-factory-tests": "^0.9",
"phpunit/phpunit": "^8.5.8 || ^9.3.10"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "George Mponos",
"email": "gmponos@gmail.com",
"homepage": "https://github.com/gmponos"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://github.com/sagikazarmark"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://sagikazarmark.hu"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
"http",
"message",
"psr-7",
"request",
"response",
"stream",
"uri",
"url"
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.2.1"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
"type": "tidelift"
}
],
"time": "2022-03-20T21:55:58+00:00"
},
{
"name": "league/oauth2-client",
"version": "2.6.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/oauth2-client.git",
"reference": "2334c249907190c132364f5dae0287ab8666aa19"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/2334c249907190c132364f5dae0287ab8666aa19",
"reference": "2334c249907190c132364f5dae0287ab8666aa19",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^6.0 || ^7.0",
"paragonie/random_compat": "^1 || ^2 || ^9.99",
"php": "^5.6 || ^7.0 || ^8.0"
},
"require-dev": {
"mockery/mockery": "^1.3.5",
"php-parallel-lint/php-parallel-lint": "^1.3.1",
"phpunit/phpunit": "^5.7 || ^6.0 || ^9.5",
"squizlabs/php_codesniffer": "^2.3 || ^3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-2.x": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"League\\OAuth2\\Client\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Alex Bilbie",
"email": "hello@alexbilbie.com",
"homepage": "http://www.alexbilbie.com",
"role": "Developer"
},
{
"name": "Woody Gilk",
"homepage": "https://github.com/shadowhand",
"role": "Contributor"
}
],
"description": "OAuth 2.0 Client Library",
"keywords": [
"Authentication",
"SSO",
"authorization",
"identity",
"idp",
"oauth",
"oauth2",
"single sign on"
],
"support": {
"issues": "https://github.com/thephpleague/oauth2-client/issues",
"source": "https://github.com/thephpleague/oauth2-client/tree/2.6.1"
},
"time": "2021-12-22T16:42:49+00:00"
},
{
"name": "lrf141/oauth2-mastodon",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/lrf141/oauth2-mastodon.git",
"reference": "ba051985643bf46788c151dc70bc8ee85edff6a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lrf141/oauth2-mastodon/zipball/ba051985643bf46788c151dc70bc8ee85edff6a4",
"reference": "ba051985643bf46788c151dc70bc8ee85edff6a4",
"shasum": ""
},
"require": {
"league/oauth2-client": "^2.0"
},
"require-dev": {
"mockery/mockery": "^1.1",
"phpunit/phpunit": ">=5.4.3",
"squizlabs/php_codesniffer": "^2.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"Lrf141\\OAuth2\\Client\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "K.Takeuchi",
"email": "ghost141.kentyo@gmail.com",
"homepage": "https://github.com/lrf141",
"role": "Developer"
}
],
"description": "Mastodon OAuth 2.0 support for the PHP League's OAuth 2.0 Client",
"homepage": "https://github.com/lrf141/oauth2-client",
"keywords": [
"authorization",
"client",
"lrf141",
"mastodon",
"oauth2",
"oauth2-mastodon"
],
"support": {
"issues": "https://github.com/lrf141/oauth2-mastodon/issues",
"source": "https://github.com/lrf141/oauth2-mastodon/tree/1.0.1"
},
"time": "2018-08-31T08:03:49+00:00"
},
{
"name": "paragonie/random_compat",
"version": "v9.99.100",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
"shasum": ""
},
"require": {
"php": ">= 7"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*",
"vimeo/psalm": "^1"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"polyfill",
"pseudorandom",
"random"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/random_compat/issues",
"source": "https://github.com/paragonie/random_compat"
},
"time": "2020-10-15T08:29:30+00:00"
},
{
"name": "psr/http-client",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0",
"psr/http-message": "^1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Client\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP clients",
"homepage": "https://github.com/php-fig/http-client",
"keywords": [
"http",
"http-client",
"psr",
"psr-18"
],
"support": {
"source": "https://github.com/php-fig/http-client/tree/master"
},
"time": "2020-06-29T06:28:15+00:00"
},
{
"name": "psr/http-factory",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
"reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
"reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
"shasum": ""
},
"require": {
"php": ">=7.0.0",
"psr/http-message": "^1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interfaces for PSR-7 HTTP message factories",
"keywords": [
"factory",
"http",
"message",
"psr",
"psr-17",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-factory/tree/master"
},
"time": "2019-04-30T12:38:16+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/master"
},
"time": "2016-08-06T14:39:51+00:00"
},
{
"name": "ralouphie/getallheaders",
"version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/ralouphie/getallheaders.git",
"reference": "120b605dfeb996808c31b6477290a714d356e822"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
"reference": "120b605dfeb996808c31b6477290a714d356e822",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^5 || ^6.5"
},
"type": "library",
"autoload": {
"files": [
"src/getallheaders.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ralph Khattar",
"email": "ralph.khattar@gmail.com"
}
],
"description": "A polyfill for getallheaders.",
"support": {
"issues": "https://github.com/ralouphie/getallheaders/issues",
"source": "https://github.com/ralouphie/getallheaders/tree/develop"
},
"time": "2019-03-08T08:55:37+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
"reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
"shasum": ""
},
"require": {
"php": ">=8.0.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.0-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-01-02T09:55:41+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.3.0"
}

View File

@ -35,7 +35,7 @@
var compressor = kompressor || 1;
var resizer = function () {
el.style.fontSize = Math.max(Math.min(el.clientWidth / (compressor*10), parseFloat(settings.maxFontSize)), parseFloat(settings.minFontSize)) + 'px';
el.style.fontSize = Math.max(Math.min(el.clientWidth / (compressor*10.5), parseFloat(settings.maxFontSize)), parseFloat(settings.minFontSize)) + 'px';
};
// Call once to set.

View File

@ -9,6 +9,7 @@
<link href="/style.css" rel="stylesheet"/>
<!-- Javascript -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/color/jquery.color.plus-names-2.1.2.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="/fittext.js"></script>
<script src="https://livejs.com/live.js"></script>
@ -20,7 +21,9 @@
<div class="desktopOnly col-4"></div>
<div id="content" class="col-4 center">
<div class="row">
<img src="/Assets/HTown.png" class="logo self-align-center mx-auto d-block" alt="Hacker Town logo in ASCII art. Rendered as image to force correct visualization."/>
<a href="https://hackers.town">
<img src="/Assets/HTown.png" class="logo self-align-center mx-auto d-block" alt="Hacker Town logo in ASCII art. Rendered as image to force correct visualization."/>
</a>
</div>
<div id="title" class="row center">
<span id="resizer">
@ -28,9 +31,10 @@
</span>
</div>
<div class="row button">
<button id="bttn">Create Account</button>
<button id="bttn" class="keyButton" onclick="beginOauth()">Create Account</button>
</div>
<div class="row copyright">
<!-- TODO: Make this file PHP and make the canary dependent on /etc/ttyserver/canary -->
Canary
<br>
Copyright ©️ 2022 Elizabeth Anne Cray

View File

@ -27,6 +27,19 @@ const resizeText = ({ element, elements, minSize = 10, maxSize = 512, step = 1,
})
}
const saveFile = (name, type, data) => {
if (data !== null && navigator.msSaveBlob)
return navigator.msSaveBlob(new Blob([data], { type: type }), name);
var a = $("<a style='display: none;'/>");
var url = window.URL.createObjectURL(new Blob([data], {type: type}));
a.attr("href", url);
a.attr("download", name);
$("body").append(a);
a[0].click();
window.URL.revokeObjectURL(url);
a.remove();
}
const disableNonDesktopElements = () => {
var disableElements = document.getElementsByClassName("desktopOnly");
for(var i=0; i< disableElements.length; i++){
@ -41,8 +54,66 @@ const disableNonDesktopElements = () => {
content.classList.add("col-10");
var te = document.getElementById("resizer");
window.fitText(te);
document.getElementById("bttn").style.height = "15vw";
var buttons = document.getElementsByClassName("keyButton");
for(var i=0; i<buttons.length; i++){
var bttn = buttons.item(i);
bttn.style.height = "15vw";
}
// document.getElementById("bttn").style.height = "15vw";
}
const generateSSH = async (name) => {
dbp("Generate Key");
generateKeyPair("RSASSA-PKCS1-v1_5", 4096, "namehere")
.then((keys) => {
console.dir(keys);
// saveFile("ssh_key.pem", "text/plain", keys[0]);
// saveFile("ssh_key.pub", "text/plain", keys[1]);
var KeyExport = new JSZip();
KeyExport.file("HackersTownTTY-"+name, keys[0]);
KeyExport.file("HackersTownTTY-"+name+".pub", keys[1]);
KeyExport.generateAsync({type:"blob"})
.then((content) => {
saveFile("HackersTownTTY-"+name+".zip", "application/zip", content);
// saveAs(content, "HackersTownTTY-"+name+".zip");
});
}).catch((err) => {
console.log(err);
});
}
const uploadSSH = () => {
}
const beginOauth = () => {
dbp("Auth");
$.ajax({
url: "https://tty.hackers.town/auth?act=id"
}).then((data) => {
console.table(data);
if(data.id){
var redirect = "https://hackers.town/oauth/authorize?"+
"response_type=code&client_id="+data.id+"&redirect_uri="+
"https://tty.hackers.town/auth&scope=read:accounts";
dbp(redirect);
window.location.href = redirect;
}else{
// Auth Failed
$("#resizer").html("AUTH FAILED");
$("#resizer").css("color", "#400112");
$("#resizer").css("background-color", "#79F257");
$("#resizer").animate({
color: "#79F257",
backgroundColor: "#022601"
}, 1000);
}
});
}
// On Page Load...
$(() => {
dbp("Begin Init Content");
// Device Detection

52
keygen.js Normal file
View File

@ -0,0 +1,52 @@
/* global encodePrivateKey, encodePublicKey */
const extractable = true;
function wrap(text, len) {
const length = len || 72;
let result = "";
for (let i = 0; i < text.length; i += length) {
result += text.slice(i, i + length);
result += "\n";
}
return result;
}
function rsaPrivateKey(key) {
return `-----BEGIN RSA PRIVATE KEY-----\n${key}-----END RSA PRIVATE KEY-----`;
}
function arrayBufferToBase64(buffer) {
let binary = "";
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i += 1) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
function generateKeyPair(alg, size, name) {
return window.crypto.subtle
.generateKey(
{
name: "RSASSA-PKCS1-v1_5",
modulusLength: 2048, // can be 1024, 2048, or 4096
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: { name: "SHA-1" }, // can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
},
extractable,
["sign", "verify"]
)
.then(key => {
const privateKey = window.crypto.subtle
.exportKey("jwk", key.privateKey)
.then(encodePrivateKey)
.then(wrap)
.then(rsaPrivateKey);
const publicKey = window.crypto.subtle.exportKey("jwk", key.publicKey).then(jwk => encodePublicKey(jwk, name));
return Promise.all([privateKey, publicKey]);
});
}
// module.exports = { arrayBufferToBase64, generateKeyPair };

84
pkcs1To8.js Normal file
View File

@ -0,0 +1,84 @@
/* eslint no-bitwise: 0 */
function wrap(text, len) {
const length = len || 72;
let result = "";
for (let i = 0; i < text.length; i += length) {
result += text.slice(i, i + length);
result += "\n";
}
return result;
}
function pemPrivateKey(key) {
return `-----BEGIN PRIVATE KEY-----\n${wrap(key, 64)}-----END PRIVATE KEY-----`;
}
function stripPemFormatting(str) {
return str
.replace(/^-----BEGIN (?:RSA )?(?:PRIVATE|PUBLIC) KEY-----$/m, "")
.replace(/^-----END (?:RSA )?(?:PRIVATE|PUBLIC) KEY-----$/m, "")
.replace(/[\n\r]/g, "");
}
function arrayToPem(a) {
return window.btoa(a.map(c => String.fromCharCode(c)).join(""));
}
function stringToArray(s) {
return s.split("").map(c => c.charCodeAt());
}
function pemToArray(pem) {
return stringToArray(window.atob(pem));
}
const prefix = [
0x30,
0x82,
0x04,
0xbc,
0x02,
0x01,
0x00,
0x30,
0x0d,
0x06,
0x09,
0x2a,
0x86,
0x48,
0x86,
0xf7,
0x0d,
0x01,
0x01,
0x01,
0x05,
0x00,
0x04,
0x82,
0x04,
0xa6,
];
function pkcs1To8(privateKeyPkcs1Pem) {
const pem = stripPemFormatting(privateKeyPkcs1Pem);
const privateKeyPkcs1Array = pemToArray(pem);
const prefixPkcs8 = prefix.concat(privateKeyPkcs1Array);
const privateKeyPkcs8Pem = arrayToPem(prefixPkcs8);
const pkcs8Pem = pemPrivateKey(privateKeyPkcs8Pem);
return pkcs8Pem;
}
// crypto.subtle.importKey(
// "spki",
// keyTextBuffer,
// {
// name: "RSASSA-PKCS1-v1_5",
// hash: { name: "SHA-256" },
// },
// true,
// ["verify"]
// );
module.exports = { pkcs1To8 };

103
publicSshToPem.js Normal file
View File

@ -0,0 +1,103 @@
/* eslint no-bitwise: 0 */
function wrap(text, len) {
const length = len || 72;
let result = "";
for (let i = 0; i < text.length; i += length) {
result += text.slice(i, i + length);
result += "\n";
}
return result;
}
function pemPublicKey(key) {
return `---- BEGIN RSA PUBLIC KEY ----\n${wrap(key, 65)}---- END RSA PUBLIC KEY ----`;
}
function integerToOctet(n) {
const result = [];
for (let i = n; i > 0; i >>= 8) {
result.push(i & 0xff);
}
return result.reverse();
}
function asnEncodeLen(n) {
let result = [];
if (n >> 7) {
result = integerToOctet(n);
result.unshift(0x80 + result.length);
} else {
result.push(n);
}
return result;
}
function checkHighestBit(v) {
if (v[0] >> 7 === 1) {
v.unshift(0); // add leading zero if first bit is set
}
return v;
}
function asn1Int(int) {
const v = checkHighestBit(int);
const len = asnEncodeLen(v.length);
return [0x02].concat(len, v); // int tag is 0x02
}
function asn1Seq(seq) {
const len = asnEncodeLen(seq.length);
return [0x30].concat(len, seq); // seq tag is 0x30
}
function arrayToPem(a) {
return window.btoa(a.map(c => String.fromCharCode(c)).join(""));
}
function arrayToString(a) {
return String.fromCharCode.apply(null, a);
}
function stringToArray(s) {
return s.split("").map(c => c.charCodeAt());
}
function pemToArray(pem) {
return stringToArray(window.atob(pem));
}
function arrayToLen(a) {
let result = 0;
for (let i = 0; i < a.length; i += 1) {
result = result * 256 + a[i];
}
return result;
}
function decodePublicKey(s) {
const split = s.split(" ");
const prefix = split[0];
if (prefix !== "ssh-rsa") {
throw new Error(`Unknown prefix: ${prefix}`);
}
const buffer = pemToArray(split[1]);
const nameLen = arrayToLen(buffer.splice(0, 4));
const type = arrayToString(buffer.splice(0, nameLen));
if (type !== "ssh-rsa") {
throw new Error(`Unknown key type: ${type}`);
}
const exponentLen = arrayToLen(buffer.splice(0, 4));
const exponent = buffer.splice(0, exponentLen);
const keyLen = arrayToLen(buffer.splice(0, 4));
const key = buffer.splice(0, keyLen);
return { type, exponent, key, name: split[2] };
}
function publicSshToPem(publicKey) {
const { key, exponent } = decodePublicKey(publicKey);
const seq = [key, exponent].map(asn1Int).reduce((acc, a) => acc.concat(a));
return pemPublicKey(arrayToPem(asn1Seq(seq)));
}
module.exports = { publicSshToPem };

56
send.php Normal file
View File

@ -0,0 +1,56 @@
<?php
exit();
$z=1/0;
use Lrf141\OAuth2\Client\Provider\Mastodon;
$config = json_decode(file_get_contents("config.json", true));
session_start();
$provider = new Mastodon([
'clientId' => $config.oauth.key,
'clientSecret' => $config.oauth.secret,
'redirectUri' => 'https://tty.hackers.town/auth',
'instance' => 'https://hackers.town',
'scope' => 'read:accounts',
]);
if (!isset($_GET['code'])) {
$authUrl = $provider->getAuthorizationUrl();
$_SESSION['oauth2state'] = $provider->getState();
header('Location: '.$authUrl);
exit;
// Check given state against previously stored one to mitigate CSRF attack
} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) {
unset($_SESSION['oauth2state']);
exit('Invalid state');
} else {
// Try to get an access token (using the authorization code grant)
$token = $provider->getAccessToken('authorization_code', [
'code' => $_GET['code']
]);
// Optional: Now you have a token you can look up a users profile data
try {
$user = $provider->getResourceOwner($token);
echo $user->getName();
} catch(Exception $e) {
exit('Oh dear...');
}
echo $token->getToken();
}
?>

121
ssh-util.js Normal file
View File

@ -0,0 +1,121 @@
/* eslint no-bitwise: 0 */
/* global base64urlDecode */
function arrayToString(a) {
return String.fromCharCode.apply(null, a);
}
function stringToArray(s) {
return s.split("").map(c => c.charCodeAt());
}
function base64urlToArray(s) {
return stringToArray(base64urlDecode(s));
}
function pemToArray(pem) {
return stringToArray(window.atob(pem));
}
function arrayToPem(a) {
return window.btoa(a.map(c => String.fromCharCode(c)).join(""));
}
function arrayToLen(a) {
let result = 0;
for (let i = 0; i < a.length; i += 1) {
result = result * 256 + a[i];
}
return result;
}
function integerToOctet(n) {
const result = [];
for (let i = n; i > 0; i >>= 8) {
result.push(i & 0xff);
}
return result.reverse();
}
function lenToArray(n) {
const oct = integerToOctet(n);
let i;
for (i = oct.length; i < 4; i += 1) {
oct.unshift(0);
}
return oct;
}
function decodePublicKey(s) {
const split = s.split(" ");
const prefix = split[0];
if (prefix !== "ssh-rsa") {
throw new Error(`Unknown prefix: ${prefix}`);
}
const buffer = pemToArray(split[1]);
const nameLen = arrayToLen(buffer.splice(0, 4));
const type = arrayToString(buffer.splice(0, nameLen));
if (type !== "ssh-rsa") {
throw new Error(`Unknown key type: ${type}`);
}
const exponentLen = arrayToLen(buffer.splice(0, 4));
const exponent = buffer.splice(0, exponentLen);
const keyLen = arrayToLen(buffer.splice(0, 4));
const key = buffer.splice(0, keyLen);
return { type, exponent, key, name: split[2] };
}
function checkHighestBit(v) {
if (v[0] >> 7 === 1) {
// add leading zero if first bit is set
v.unshift(0);
}
return v;
}
function jwkToInternal(jwk) {
return {
type: "ssh-rsa",
exponent: checkHighestBit(stringToArray(base64urlDecode(jwk.e))),
name: "name",
key: checkHighestBit(stringToArray(base64urlDecode(jwk.n))),
};
}
function encodePublicKey(jwk, name) {
const k = jwkToInternal(jwk);
k.name = name;
const keyLenA = lenToArray(k.key.length);
const exponentLenA = lenToArray(k.exponent.length);
const typeLenA = lenToArray(k.type.length);
const array = [].concat(typeLenA, stringToArray(k.type), exponentLenA, k.exponent, keyLenA, k.key);
const encoding = arrayToPem(array);
return `${k.type} ${encoding} ${k.name}`;
}
function asnEncodeLen(n) {
let result = [];
if (n >> 7) {
result = integerToOctet(n);
result.unshift(0x80 + result.length);
} else {
result.push(n);
}
return result;
}
function encodePrivateKey(jwk) {
const order = ["n", "e", "d", "p", "q", "dp", "dq", "qi"];
const list = order.map(prop => {
const v = checkHighestBit(stringToArray(base64urlDecode(jwk[prop])));
const len = asnEncodeLen(v.length);
return [0x02].concat(len, v); // int tag is 0x02
});
let seq = [0x02, 0x01, 0x00]; // extra seq for SSH
seq = seq.concat(...list);
const len = asnEncodeLen(seq.length);
const a = [0x30].concat(len, seq); // seq is 0x30
return arrayToPem(a);
}
module.exports = { base64urlToArray, decodePublicKey, encodePublicKey, encodePrivateKey };

View File

@ -23,7 +23,7 @@ body {
height: 80%;
margin: 2vw;
padding: 2vw;
color: white;
color: #79F257;
border: 2px solid #377326;
border-radius: 1em;
}
@ -43,6 +43,12 @@ span {
display: block;
}
.message {
width: 100%;
text-align: center;
display: block;
}
.button {
margin: 8px;
}
@ -59,4 +65,8 @@ span {
text-align: center;
display: block;
color: #377326;
}
.keyButton {
margin: 1px;
}