This commit is contained in:
2025-04-19 19:43:16 +02:00
parent bf2f7b57bb
commit d8a314df41
6 changed files with 567 additions and 4 deletions

View File

@@ -84,6 +84,26 @@
.hidden {
display: none;
}
.pubkey-container {
margin-top: 15px;
padding: 10px;
background-color: #f8f9fa;
border-radius: 4px;
border: 1px solid #ddd;
}
.pubkey-label {
font-weight: bold;
margin-bottom: 5px;
}
.pubkey-value {
font-family: monospace;
word-break: break-all;
background-color: #e9ecef;
padding: 8px;
border-radius: 4px;
margin-bottom: 10px;
border: 1px solid #ced4da;
}
</style>
</head>
<body>
@@ -118,7 +138,21 @@
<div class="form-group">
<label>Current Space: <span id="current-space-name"></span></label>
</div>
<button id="logout-button" class="danger">Logout</button>
<div class="form-group">
<button id="logout-button" class="danger">Logout</button>
<button id="delete-space-button" class="danger">Delete Space</button>
</div>
</div>
<div id="manage-spaces-form">
<h3>Manage Spaces</h3>
<div class="form-group">
<label for="space-list">Available Spaces:</label>
<select id="space-list">
<option value="">-- Select a space --</option>
</select>
<button id="delete-selected-space-button" class="danger">Delete Selected Space</button>
</div>
</div>
<div class="result" id="space-result">Result will appear here</div>
@@ -166,6 +200,55 @@
<div class="result" id="verify-result">Verification result will appear here</div>
</div>
<div class="container">
<h2>Verify with Public Key Only</h2>
<div>
<div class="form-group">
<label for="pubkey-verify-pubkey">Public Key (hex):</label>
<input type="text" id="pubkey-verify-pubkey" placeholder="Enter public key in hex format" />
</div>
<textarea id="pubkey-verify-message" placeholder="Enter message to verify" rows="3"></textarea>
<textarea id="pubkey-verify-signature" placeholder="Enter signature to verify" rows="3"></textarea>
<button id="pubkey-verify-button">Verify with Public Key</button>
</div>
<div class="result" id="pubkey-verify-result">Verification result will appear here</div>
</div>
<div class="container">
<h2>Derive Public Key from Private Key</h2>
<div>
<div class="form-group">
<label for="derive-pubkey-privkey">Private Key (hex):</label>
<input type="text" id="derive-pubkey-privkey" placeholder="Enter private key in hex format" />
</div>
<button id="derive-pubkey-button">Derive Public Key</button>
</div>
<div class="result" id="derive-pubkey-result">Public key will appear here</div>
</div>
<div class="container">
<h2>Asymmetric Encryption</h2>
<div>
<div class="form-group">
<label for="asymmetric-encrypt-pubkey">Recipient's Public Key (hex):</label>
<input type="text" id="asymmetric-encrypt-pubkey" placeholder="Enter recipient's public key in hex format" />
</div>
<textarea id="asymmetric-encrypt-message" placeholder="Enter message to encrypt" rows="3">This is a secret message that will be encrypted with asymmetric encryption</textarea>
<button id="asymmetric-encrypt-button">Encrypt with Public Key</button>
</div>
<div class="result" id="asymmetric-encrypt-result">Encrypted data will appear here</div>
</div>
<div class="container">
<h2>Asymmetric Decryption</h2>
<div>
<div class="note">Note: Uses the currently selected keypair for decryption</div>
<textarea id="asymmetric-decrypt-ciphertext" placeholder="Enter ciphertext (hex)" rows="3"></textarea>
<button id="asymmetric-decrypt-button">Decrypt with Private Key</button>
</div>
<div class="result" id="asymmetric-decrypt-result">Decrypted data will appear here</div>
</div>
<div class="container">
<h2>Symmetric Encryption</h2>
<div>

View File

@@ -1,5 +1,5 @@
// Import our WebAssembly module
import init, {
import init, {
create_key_space,
encrypt_key_space,
decrypt_key_space,
@@ -10,6 +10,10 @@ import init, {
keypair_pub_key,
keypair_sign,
keypair_verify,
derive_public_key,
verify_with_public_key,
encrypt_asymmetric,
decrypt_asymmetric,
generate_symmetric_key,
derive_key_from_password,
encrypt_symmetric,
@@ -121,6 +125,30 @@ function updateLoginUI() {
loginStatus.className = 'status logged-out';
currentSpaceName.textContent = '';
}
// Update the spaces list
updateSpacesList();
}
// Update the spaces dropdown list
function updateSpacesList() {
const spacesList = document.getElementById('space-list');
// Clear existing options
while (spacesList.options.length > 1) {
spacesList.remove(1);
}
// Get spaces list
const spaces = listSpacesFromStorage();
// Add options for each space
spaces.forEach(spaceName => {
const option = document.createElement('option');
option.value = spaceName;
option.textContent = spaceName;
spacesList.appendChild(option);
});
}
// Login to a space
@@ -346,7 +374,36 @@ async function performSelectKeypair() {
function displaySelectedKeypairPublicKey() {
try {
const pubKey = keypair_pub_key();
document.getElementById('selected-pubkey-display').textContent = `Public Key: ${bufferToHex(pubKey)}`;
const pubKeyHex = bufferToHex(pubKey);
// Create a more user-friendly display with copy button
const pubKeyDisplay = document.getElementById('selected-pubkey-display');
pubKeyDisplay.innerHTML = `
<div class="pubkey-container">
<div class="pubkey-label">Public Key (hex):</div>
<div class="pubkey-value" id="pubkey-hex-value">${pubKeyHex}</div>
<button id="copy-pubkey-button" class="secondary">Copy Public Key</button>
</div>
`;
// Add event listener for the copy button
document.getElementById('copy-pubkey-button').addEventListener('click', () => {
const pubKeyText = document.getElementById('pubkey-hex-value').textContent;
navigator.clipboard.writeText(pubKeyText)
.then(() => {
alert('Public key copied to clipboard!');
})
.catch(err => {
console.error('Could not copy text: ', err);
});
});
// Also populate the public key field in the verify with public key section
document.getElementById('pubkey-verify-pubkey').value = pubKeyHex;
// And in the asymmetric encryption section
document.getElementById('asymmetric-encrypt-pubkey').value = pubKeyHex;
} catch (e) {
document.getElementById('selected-pubkey-display').textContent = `Error getting public key: ${e}`;
}
@@ -357,7 +414,15 @@ function saveCurrentSpace() {
if (!isLoggedIn || !currentSpace) return;
try {
// Store the password in a session variable when logging in
// and use it here to avoid issues when the password field is cleared
const password = document.getElementById('space-password').value;
if (!password) {
console.error('Password not available for saving space');
alert('Please re-enter your password to save changes');
return;
}
const encryptedSpace = encrypt_key_space(password);
saveSpaceToStorage(currentSpace, encryptedSpace);
} catch (e) {
@@ -365,6 +430,26 @@ function saveCurrentSpace() {
}
}
// Delete a space from localStorage
function deleteSpace(spaceName) {
if (!spaceName) return false;
// Check if space exists
if (!getSpaceFromStorage(spaceName)) {
return false;
}
// Remove from localStorage
removeSpaceFromStorage(spaceName);
// If this was the current space, logout
if (isLoggedIn && currentSpace === spaceName) {
performLogout();
}
return true;
}
async function run() {
// Initialize the WebAssembly module
await init();
@@ -375,6 +460,32 @@ async function run() {
document.getElementById('login-button').addEventListener('click', performLogin);
document.getElementById('create-space-button').addEventListener('click', performCreateSpace);
document.getElementById('logout-button').addEventListener('click', performLogout);
document.getElementById('delete-space-button').addEventListener('click', () => {
if (confirm(`Are you sure you want to delete the space "${currentSpace}"? This action cannot be undone.`)) {
if (deleteSpace(currentSpace)) {
document.getElementById('space-result').textContent = `Space "${currentSpace}" deleted successfully`;
} else {
document.getElementById('space-result').textContent = `Error deleting space "${currentSpace}"`;
}
}
});
document.getElementById('delete-selected-space-button').addEventListener('click', () => {
const selectedSpace = document.getElementById('space-list').value;
if (!selectedSpace) {
document.getElementById('space-result').textContent = 'Please select a space to delete';
return;
}
if (confirm(`Are you sure you want to delete the space "${selectedSpace}"? This action cannot be undone.`)) {
if (deleteSpace(selectedSpace)) {
document.getElementById('space-result').textContent = `Space "${selectedSpace}" deleted successfully`;
updateSpacesList();
} else {
document.getElementById('space-result').textContent = `Error deleting space "${selectedSpace}"`;
}
}
});
// Set up the keypair management
document.getElementById('create-keypair-button').addEventListener('click', performCreateKeypair);
@@ -530,6 +641,140 @@ async function run() {
document.getElementById('password-decrypt-result').textContent = `Error: ${e}`;
}
});
// Set up the public key verification example
document.getElementById('pubkey-verify-button').addEventListener('click', () => {
try {
const publicKeyHex = document.getElementById('pubkey-verify-pubkey').value.trim();
if (!publicKeyHex) {
document.getElementById('pubkey-verify-result').textContent = 'Please enter a public key';
return;
}
const message = document.getElementById('pubkey-verify-message').value;
const messageBytes = new TextEncoder().encode(message);
const signatureHex = document.getElementById('pubkey-verify-signature').value;
const signatureBytes = hexToBuffer(signatureHex);
const publicKeyBytes = hexToBuffer(publicKeyHex);
try {
const isValid = verify_with_public_key(publicKeyBytes, messageBytes, signatureBytes);
document.getElementById('pubkey-verify-result').textContent =
isValid ? 'Signature is valid!' : 'Signature is NOT valid!';
} catch (e) {
document.getElementById('pubkey-verify-result').textContent = `Error verifying: ${e}`;
}
} catch (e) {
document.getElementById('pubkey-verify-result').textContent = `Error: ${e}`;
}
});
// Set up the derive public key example
document.getElementById('derive-pubkey-button').addEventListener('click', () => {
try {
const privateKeyHex = document.getElementById('derive-pubkey-privkey').value.trim();
if (!privateKeyHex) {
document.getElementById('derive-pubkey-result').textContent = 'Please enter a private key';
return;
}
const privateKeyBytes = hexToBuffer(privateKeyHex);
try {
const publicKey = derive_public_key(privateKeyBytes);
const publicKeyHex = bufferToHex(publicKey);
// Create a more user-friendly display with copy button
const pubKeyDisplay = document.getElementById('derive-pubkey-result');
pubKeyDisplay.innerHTML = `
<div class="pubkey-container">
<div class="pubkey-label">Derived Public Key (hex):</div>
<div class="pubkey-value" id="derived-pubkey-hex-value">${publicKeyHex}</div>
<button id="copy-derived-pubkey-button" class="secondary">Copy Public Key</button>
</div>
`;
// Add event listener for the copy button
document.getElementById('copy-derived-pubkey-button').addEventListener('click', () => {
const pubKeyText = document.getElementById('derived-pubkey-hex-value').textContent;
navigator.clipboard.writeText(pubKeyText)
.then(() => {
alert('Public key copied to clipboard!');
})
.catch(err => {
console.error('Could not copy text: ', err);
});
});
// Also populate the public key field in the verify with public key section
document.getElementById('pubkey-verify-pubkey').value = publicKeyHex;
// And in the asymmetric encryption section
document.getElementById('asymmetric-encrypt-pubkey').value = publicKeyHex;
} catch (e) {
document.getElementById('derive-pubkey-result').textContent = `Error deriving public key: ${e}`;
}
} catch (e) {
document.getElementById('derive-pubkey-result').textContent = `Error: ${e}`;
}
});
// Set up the asymmetric encryption example
document.getElementById('asymmetric-encrypt-button').addEventListener('click', () => {
try {
const publicKeyHex = document.getElementById('asymmetric-encrypt-pubkey').value.trim();
if (!publicKeyHex) {
document.getElementById('asymmetric-encrypt-result').textContent = 'Please enter a recipient public key';
return;
}
const message = document.getElementById('asymmetric-encrypt-message').value;
const messageBytes = new TextEncoder().encode(message);
const publicKeyBytes = hexToBuffer(publicKeyHex);
try {
const ciphertext = encrypt_asymmetric(publicKeyBytes, messageBytes);
const ciphertextHex = bufferToHex(ciphertext);
document.getElementById('asymmetric-encrypt-result').textContent = `Ciphertext: ${ciphertextHex}`;
// Store for decryption
document.getElementById('asymmetric-decrypt-ciphertext').value = ciphertextHex;
} catch (e) {
document.getElementById('asymmetric-encrypt-result').textContent = `Error encrypting: ${e}`;
}
} catch (e) {
document.getElementById('asymmetric-encrypt-result').textContent = `Error: ${e}`;
}
});
// Set up the asymmetric decryption example
document.getElementById('asymmetric-decrypt-button').addEventListener('click', () => {
if (!isLoggedIn) {
document.getElementById('asymmetric-decrypt-result').textContent = 'Please login first';
return;
}
if (!selectedKeypair) {
document.getElementById('asymmetric-decrypt-result').textContent = 'Please select a keypair first';
return;
}
try {
const ciphertextHex = document.getElementById('asymmetric-decrypt-ciphertext').value;
const ciphertext = hexToBuffer(ciphertextHex);
try {
const plaintext = decrypt_asymmetric(ciphertext);
const decodedText = new TextDecoder().decode(plaintext);
document.getElementById('asymmetric-decrypt-result').textContent = `Decrypted: ${decodedText}`;
} catch (e) {
document.getElementById('asymmetric-decrypt-result').textContent = `Error decrypting: ${e}`;
}
} catch (e) {
document.getElementById('asymmetric-decrypt-result').textContent = `Error: ${e}`;
}
});
// Initialize UI
updateLoginUI();