-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding the application for the Schnitzeljagd
- Loading branch information
Showing
3 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
--> |