Skip to content

Commit

Permalink
Inital commit
Browse files Browse the repository at this point in the history
Adding the application for the Schnitzeljagd
  • Loading branch information
jon011235 authored Feb 15, 2024
1 parent eddb18f commit 0e8a07f
Show file tree
Hide file tree
Showing 3 changed files with 313 additions and 0 deletions.
67 changes: 67 additions & 0 deletions app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
body {
margin: 0;
padding: 0;
}

#map {
height: 100vh;
}

.search-container {
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
background-color: white;
padding: 10px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

input[type="text"] {
padding: 8px;
border: 1px solid #ccc;
border-radius: 5px;
}

button {
padding: 8px 16px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}

button:hover {
background-color: #0056b3;
}

.buttons {
display: flex;
flex-direction: column;
gap: 20px;
}
.button {
padding: 15px 30px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
text-align: center;
text-decoration: none;
font-size: 18px;
transition: background-color 0.3s ease;
}
.button:hover {
background-color: #45a049;
}


.start-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
200 changes: 200 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
//Setup globals
data = {}

var map = L.map('map').setView([49.442778, 7.896667], 13);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,}).addTo(map);

//process URL parameters
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
if (urlParams.has('camp')){
console.log(typeof(urlParams.get('camp')));
switch (urlParams.get('camp')) {
case "pfalz":
pfalz();
break;
case "stingbert":
stingbert();
break;
default:
alert("unkown camp");
break;
}
}

async function searchLocation() {
var guess = document.getElementById("searchInput").value;
for (const entry of data["locations"]) {
if (entry.answer == (await sha256(guess))) {
result = await decrypt(entry.secret, guess);
data = JSON.parse(result);
var marker = L.marker([data["lat"], data["lon"]]).addTo(map);
marker.bindPopup(data["message"])//.openPopup(); //TODO should it open automatically?
return;
}
}
alert("Kein Treffer");

}

function hide_start_screen(){
document.getElementById("start-container").style.display="none"
document.getElementById("schnitzeljagd").removeAttribute("hidden");
}

//TODO given parameters on URL do this automatically
function setup_schnitzeljagd(json){
hide_start_screen();
map.setView(data.position, 15)
}

function pfalz(){
data = {"locations":
[
{
answer: "bffa164be2502c9fa31e1a50ffb75c7b3b9982bd6c27a5f43208a2559d7ed588",//rot
secret: "UmHVK5XYQu1G33YqzjUDfogvtcbQ3W4Vnrhyj8Oybcs15dqDPsdM+7C99RliW9KVbOB1Xc8OeY3P7stQrVQTqXJDO6sktxFtUSUxCc0P1HnIgYKd9yo="
},
{
"answer": "bffa164be2502c9fa31e1a50ffb75c7b3b9982bd6c27a5f43208a2559d7ed588",
"secret": "IAvicABDsl5OUI2p16tnIxNY2uvq8syosPrerpk7656bcL7OAUKKQLp8ZJuB1ZMa0QPq5ZRrdjKSpEqiEISN25UdpktuJUEfGkohDONQZuMKfYe1Z98="
},
//Add more as needed
],
"position": [49.442778, 7.896667]
};
setup_schnitzeljagd(data);
}

function stingbert(){
data = {"locations":
[
{
answer: "echo",
secret: "U2FsdGVkX1+MgkXaWVJK9M7u+PnrgUhJF5jqcB8jfyE="
},
{
answer: "footsteps",
secret: "U2FsdGVkX1+nkGo3jPY0HMb+Sl0t1CJ+w4V3R+eoWgU="
},
//Add more as needed
],
"position": [49.278889, 7.115]
};
setup_schnitzeljagd(data);
}

function create_location(answer, lat, lon, message){
json = '{"lat": "'+lat+'","lon": "'+lon+'","message": "'+message+'"}'
sha256(answer).then(hash =>
encrypt(json, answer).then(result=>console.log({
answer: hash,
secret: result
},))
);
}

async function sha256(message) {
// Convert message to ArrayBuffer
const encoder = new TextEncoder();
const data = encoder.encode(message);

// Generate hash
const hashBuffer = await crypto.subtle.digest('SHA-256', data);

// Convert ArrayBuffer to hex string
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');

return hashHex;
}

// Function to decrypt the secret
async function decrypt(encryptedText, password) {
// Decode Base64
const encryptedArray = Uint8Array.from(atob(encryptedText), c => c.charCodeAt(0));

// Derive key from password
const keyMaterial = await window.crypto.subtle.importKey(
"raw",
new TextEncoder().encode(password),
{ name: "PBKDF2" },
false,
["deriveKey"]
);
const key = await window.crypto.subtle.deriveKey(
{
"name": "PBKDF2",
salt: new Uint8Array([84, 211, 107, 16, 247, 146, 89, 22, 33, 218, 155, 71, 91, 206, 90, 97]),
iterations: 100000,
hash: "SHA-256"
},
keyMaterial,
{ name: "AES-GCM", length: 256 },
true,
["decrypt"]
);

// Decrypt using AES-GCM
const decryptedArray = await window.crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: encryptedArray.slice(0, 12) // IV length for AES-GCM is 12 bytes
},
key,
encryptedArray.slice(12) // Remove IV from the encrypted array
);

return new TextDecoder().decode(decryptedArray);
}

async function encrypt(secret, password) {
const encoder = new TextEncoder();
secret = encoder.encode(secret).buffer;

// Generate a random initialization vector (IV)
const iv = window.crypto.getRandomValues(new Uint8Array(12)); // 12 bytes = 96 bits (recommended for AES-GCM)

const keyMaterial = await window.crypto.subtle.importKey(
"raw",
new TextEncoder().encode(password),
{ name: "PBKDF2" },
false,
["deriveKey"]
);

// Derive a random encryption key
const key = await window.crypto.subtle.deriveKey(
{
"name": "PBKDF2",
salt: new Uint8Array([84, 211, 107, 16, 247, 146, 89, 22, 33, 218, 155, 71, 91, 206, 90, 97]),
iterations: 100000,
hash: "SHA-256"
},
keyMaterial,
{ name: "AES-GCM", length: 256 },
true,
["decrypt", "encrypt"]
);

// Encrypt the secret using AES-GCM
const encryptedData = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: iv
},
key,
secret
);

// Combine IV and encrypted data
const ivAndEncryptedData = new Uint8Array([...iv, ...new Uint8Array(encryptedData)]);

// Encode the combined data as Base64
const base64Encoded = btoa(String.fromCharCode.apply(null, ivAndEncryptedData));

// Display the Base64-encoded encrypted secret
return base64Encoded
}
46 changes: 46 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🗺️</text></svg>">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Schnitzeljagd</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
<link rel="stylesheet" type="text/css" href="app.css">
</head>
<body>
<div class="start-container" id="start-container">
<div class="buttons">
<a onclick="pfalz()" class="button">BWINF Wintercamp Pfalz</a>
<a onclick="stingbert()" class="button">Jugendwettbewerb Camp</a>
<!--<a onclick="test()" class="button">Potenziell andere?</a>-->
</div>
</div>
<div id="schnitzeljagd" hidden>
<div id="map"></div>
<div class="search-container">
<input type="text" id="searchInput" placeholder="Raten">
<button onclick="searchLocation()">Submit</button>
</div>

<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script src="app.js"></script>
</div>
</body>
</html>
<!-- How to add a new location:
1) copy the pfalz function and rename it
2) change the data json
2.1) change the position
2.2) remove all existing locations
2.3) add as many locations as wished by
copy and pasting the output of create_location(answer, lat, lon, message)
where "answer" is the clue the people should enter in the search field
copy it into the array (and dont forget commas at the end)
3) add a Button
3.1) Add ```<a onclick="test()" class="button">Potenziell andere?</a>``` in the index.html
3.2) change name and called function
4) in app.js add your camp name and function to the switch statement like this:
case "stingbert":
stingbert()
-->

0 comments on commit 0e8a07f

Please sign in to comment.