Compare commits
2 Commits
3b657e3137
...
4b9e46b212
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b9e46b212 | |||
| b970e60de1 |
60
app/index.js
60
app/index.js
@@ -19,6 +19,66 @@ $(document).ready(function () {
|
|||||||
window.open( url, target );
|
window.open( url, target );
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.unlock-pin').click(async function() {
|
||||||
|
const btn = $(this);
|
||||||
|
const pinDisplay = btn.siblings('.pin-display');
|
||||||
|
const ciphertext = btn.data('ciphertext');
|
||||||
|
const iv = btn.data('iv');
|
||||||
|
const salt = btn.data('salt');
|
||||||
|
const tag = btn.data('tag');
|
||||||
|
|
||||||
|
const password = prompt("Enter the members password to unlock the PIN:");
|
||||||
|
if (!password) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decrypted = await decryptPin(ciphertext, iv, salt, tag, password);
|
||||||
|
pinDisplay.val(decrypted);
|
||||||
|
btn.text('Copy').removeClass('unlock-pin').attr('action', 'copy').attr('value', decrypted);
|
||||||
|
// Re-bind the click event for the new copy action (or use delegation)
|
||||||
|
btn.off('click').click(function() {
|
||||||
|
navigator.clipboard.writeText($(this).attr('value'));
|
||||||
|
alert('PIN copied to clipboard!');
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
alert(e.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function decryptPin(ciphertextBase64, ivBase64, saltBase64, tagBase64, password) {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const passwordKey = await crypto.subtle.importKey(
|
||||||
|
"raw", encoder.encode(password), "PBKDF2", false, ["deriveKey"]
|
||||||
|
);
|
||||||
|
const salt = Uint8Array.from(atob(saltBase64), c => c.charCodeAt(0));
|
||||||
|
const iv = Uint8Array.from(atob(ivBase64), c => c.charCodeAt(0));
|
||||||
|
const tag = Uint8Array.from(atob(tagBase64), c => c.charCodeAt(0));
|
||||||
|
const ciphertext = Uint8Array.from(atob(ciphertextBase64), c => c.charCodeAt(0));
|
||||||
|
|
||||||
|
const data = new Uint8Array(ciphertext.length + tag.length);
|
||||||
|
data.set(ciphertext);
|
||||||
|
data.set(tag, ciphertext.length);
|
||||||
|
|
||||||
|
const key = await crypto.subtle.deriveKey(
|
||||||
|
{ name: "PBKDF2", salt, iterations: 100000, hash: "SHA-256" },
|
||||||
|
passwordKey,
|
||||||
|
{ name: "AES-GCM", length: 256 },
|
||||||
|
false,
|
||||||
|
["decrypt"]
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decrypted = await crypto.subtle.decrypt(
|
||||||
|
{ name: "AES-GCM", iv, tagLength: 128 },
|
||||||
|
key,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
return new TextDecoder().decode(decrypted);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error("Decryption failed. Probably wrong password.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var isPhone = /Mobi|Android|iPhone|iPod/i.test(navigator.userAgent) || window.innerWidth <= 768;
|
var isPhone = /Mobi|Android|iPhone|iPod/i.test(navigator.userAgent) || window.innerWidth <= 768;
|
||||||
if (isPhone) {
|
if (isPhone) {
|
||||||
$("body").addClass("phone");
|
$("body").addClass("phone");
|
||||||
|
|||||||
@@ -137,6 +137,24 @@ use Michelf\Markdown;
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
|
<?php if ( isset($event["pin"]) ) { ?>
|
||||||
|
<div class="input-group mt-3 mb-3">
|
||||||
|
<span class="input-group-text">PIN</span>
|
||||||
|
<?php if ( is_array($event["pin"]) ) { ?>
|
||||||
|
<input type="text" class="form-control pin-display" disabled value="********">
|
||||||
|
<button class="btn btn-outline-secondary unlock-pin" type="button"
|
||||||
|
data-ciphertext="<?= $event["pin"]["ciphertext"] ?>"
|
||||||
|
data-iv="<?= $event["pin"]["iv"] ?>"
|
||||||
|
data-salt="<?= $event["pin"]["salt"] ?>"
|
||||||
|
data-tag="<?= $event["pin"]["tag"] ?>">
|
||||||
|
Unlock
|
||||||
|
</button>
|
||||||
|
<?php } else { ?>
|
||||||
|
<input type="text" class="form-control" disabled value="<?= $event["pin"] ?>">
|
||||||
|
<button class="btn btn-outline-secondary" type="button" action="copy" value="<?= $event["pin"] ?>">Copy</button>
|
||||||
|
<?php } ?>
|
||||||
|
</div>
|
||||||
|
<?php } ?>
|
||||||
<?php if ( in_array( "description", array_keys($event) ) ) { ?>
|
<?php if ( in_array( "description", array_keys($event) ) ) { ?>
|
||||||
<h5 class="card-title mb-2">Description</h5>
|
<h5 class="card-title mb-2">Description</h5>
|
||||||
<p><?= $event["description"] ?></p>
|
<p><?= $event["description"] ?></p>
|
||||||
|
|||||||
@@ -5,7 +5,13 @@
|
|||||||
"end": "2026-07-23T21:30",
|
"end": "2026-07-23T21:30",
|
||||||
"title": "July 2026 In Person Catch Up",
|
"title": "July 2026 In Person Catch Up",
|
||||||
"description": "Monthly In Person Get Together. Check Discourse for room details.",
|
"description": "Monthly In Person Get Together. Check Discourse for room details.",
|
||||||
"location": "Carindale Library"
|
"location": "Carindale Library",
|
||||||
|
"pin": {
|
||||||
|
"ciphertext": "XUFjTcV3",
|
||||||
|
"iv": "WtpvNa5aSMRMHoVL",
|
||||||
|
"salt": "jEZ5NzPNSCnhuBzmdybmkw==",
|
||||||
|
"tag": "cD15oQ0UJM5UNQ8Wb8GlIg=="
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"coordinates": "",
|
"coordinates": "",
|
||||||
@@ -21,7 +27,13 @@
|
|||||||
"end": "2026-06-25T21:30",
|
"end": "2026-06-25T21:30",
|
||||||
"title": "June 2026 In Person Catch Up",
|
"title": "June 2026 In Person Catch Up",
|
||||||
"description": "Monthly In Person Get Together. Check Discourse for room details.",
|
"description": "Monthly In Person Get Together. Check Discourse for room details.",
|
||||||
"location": "Carindale Library"
|
"location": "Carindale Library",
|
||||||
|
"pin": {
|
||||||
|
"ciphertext": "XUFjTcV3",
|
||||||
|
"iv": "WtpvNa5aSMRMHoVL",
|
||||||
|
"salt": "jEZ5NzPNSCnhuBzmdybmkw==",
|
||||||
|
"tag": "cD15oQ0UJM5UNQ8Wb8GlIg=="
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"coordinates": "",
|
"coordinates": "",
|
||||||
|
|||||||
Reference in New Issue
Block a user