...
This commit is contained in:
442
www/js/index.js
442
www/js/index.js
@@ -1,12 +1,21 @@
|
||||
// Import our WebAssembly module
|
||||
import init, {
|
||||
keypair_new,
|
||||
create_key_space,
|
||||
encrypt_key_space,
|
||||
decrypt_key_space,
|
||||
logout,
|
||||
create_keypair,
|
||||
select_keypair,
|
||||
list_keypairs,
|
||||
keypair_pub_key,
|
||||
keypair_sign,
|
||||
keypair_verify,
|
||||
generate_symmetric_key,
|
||||
derive_key_from_password,
|
||||
encrypt_symmetric,
|
||||
decrypt_symmetric
|
||||
decrypt_symmetric,
|
||||
encrypt_with_password,
|
||||
decrypt_with_password
|
||||
} from '../../pkg/webassembly.js';
|
||||
|
||||
// Helper function to convert ArrayBuffer to hex string
|
||||
@@ -25,36 +34,364 @@ function hexToBuffer(hex) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Session management
|
||||
let lastActivity = Date.now();
|
||||
let logoutTimer = null;
|
||||
const AUTO_LOGOUT_TIME = 15 * 60 * 1000; // 15 minutes
|
||||
|
||||
// Update last activity timestamp
|
||||
function updateActivity() {
|
||||
lastActivity = Date.now();
|
||||
}
|
||||
|
||||
// Check for inactivity and logout if needed
|
||||
function checkInactivity() {
|
||||
const inactiveTime = Date.now() - lastActivity;
|
||||
if (inactiveTime > AUTO_LOGOUT_TIME) {
|
||||
performLogout();
|
||||
alert('You have been logged out due to inactivity.');
|
||||
}
|
||||
}
|
||||
|
||||
// Setup auto-logout timer
|
||||
function setupAutoLogout() {
|
||||
logoutTimer = setInterval(checkInactivity, 60000); // Check every minute
|
||||
}
|
||||
|
||||
// Clear auto-logout timer
|
||||
function clearAutoLogout() {
|
||||
if (logoutTimer) {
|
||||
clearInterval(logoutTimer);
|
||||
logoutTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// LocalStorage functions for key spaces
|
||||
const STORAGE_PREFIX = 'crypto_space_';
|
||||
|
||||
// Save encrypted space to localStorage
|
||||
function saveSpaceToStorage(spaceName, encryptedData) {
|
||||
localStorage.setItem(`${STORAGE_PREFIX}${spaceName}`, encryptedData);
|
||||
}
|
||||
|
||||
// Get encrypted space from localStorage
|
||||
function getSpaceFromStorage(spaceName) {
|
||||
return localStorage.getItem(`${STORAGE_PREFIX}${spaceName}`);
|
||||
}
|
||||
|
||||
// List all spaces in localStorage
|
||||
function listSpacesFromStorage() {
|
||||
const spaces = [];
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if (key.startsWith(STORAGE_PREFIX)) {
|
||||
spaces.push(key.substring(STORAGE_PREFIX.length));
|
||||
}
|
||||
}
|
||||
return spaces;
|
||||
}
|
||||
|
||||
// Remove space from localStorage
|
||||
function removeSpaceFromStorage(spaceName) {
|
||||
localStorage.removeItem(`${STORAGE_PREFIX}${spaceName}`);
|
||||
}
|
||||
|
||||
// Session state
|
||||
let isLoggedIn = false;
|
||||
let currentSpace = null;
|
||||
let selectedKeypair = null;
|
||||
|
||||
// Update UI based on login state
|
||||
function updateLoginUI() {
|
||||
const loginForm = document.getElementById('login-form');
|
||||
const logoutForm = document.getElementById('logout-form');
|
||||
const loginStatus = document.getElementById('login-status');
|
||||
const currentSpaceName = document.getElementById('current-space-name');
|
||||
|
||||
if (isLoggedIn) {
|
||||
loginForm.classList.add('hidden');
|
||||
logoutForm.classList.remove('hidden');
|
||||
loginStatus.textContent = 'Status: Logged in';
|
||||
loginStatus.className = 'status logged-in';
|
||||
currentSpaceName.textContent = currentSpace;
|
||||
} else {
|
||||
loginForm.classList.remove('hidden');
|
||||
logoutForm.classList.add('hidden');
|
||||
loginStatus.textContent = 'Status: Not logged in';
|
||||
loginStatus.className = 'status logged-out';
|
||||
currentSpaceName.textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Login to a space
|
||||
async function performLogin() {
|
||||
const spaceName = document.getElementById('space-name').value.trim();
|
||||
const password = document.getElementById('space-password').value;
|
||||
|
||||
if (!spaceName || !password) {
|
||||
document.getElementById('space-result').textContent = 'Please enter both space name and password';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Get encrypted space from localStorage
|
||||
const encryptedSpace = getSpaceFromStorage(spaceName);
|
||||
if (!encryptedSpace) {
|
||||
document.getElementById('space-result').textContent = `Space "${spaceName}" not found`;
|
||||
return;
|
||||
}
|
||||
|
||||
// Decrypt the space
|
||||
const result = decrypt_key_space(encryptedSpace, password);
|
||||
if (result === 0) {
|
||||
isLoggedIn = true;
|
||||
currentSpace = spaceName;
|
||||
updateLoginUI();
|
||||
updateKeypairsList();
|
||||
document.getElementById('space-result').textContent = `Successfully logged in to space "${spaceName}"`;
|
||||
|
||||
// Setup auto-logout
|
||||
updateActivity();
|
||||
setupAutoLogout();
|
||||
|
||||
// Add activity listeners
|
||||
document.addEventListener('click', updateActivity);
|
||||
document.addEventListener('keypress', updateActivity);
|
||||
} else {
|
||||
document.getElementById('space-result').textContent = `Error logging in: ${result}`;
|
||||
}
|
||||
} catch (e) {
|
||||
document.getElementById('space-result').textContent = `Error: ${e}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new space
|
||||
async function performCreateSpace() {
|
||||
const spaceName = document.getElementById('space-name').value.trim();
|
||||
const password = document.getElementById('space-password').value;
|
||||
|
||||
if (!spaceName || !password) {
|
||||
document.getElementById('space-result').textContent = 'Please enter both space name and password';
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if space already exists
|
||||
if (getSpaceFromStorage(spaceName)) {
|
||||
document.getElementById('space-result').textContent = `Space "${spaceName}" already exists`;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Create new space
|
||||
const result = create_key_space(spaceName);
|
||||
if (result === 0) {
|
||||
// Encrypt and save the space
|
||||
const encryptedSpace = encrypt_key_space(password);
|
||||
saveSpaceToStorage(spaceName, encryptedSpace);
|
||||
|
||||
isLoggedIn = true;
|
||||
currentSpace = spaceName;
|
||||
updateLoginUI();
|
||||
updateKeypairsList();
|
||||
document.getElementById('space-result').textContent = `Successfully created space "${spaceName}"`;
|
||||
|
||||
// Setup auto-logout
|
||||
updateActivity();
|
||||
setupAutoLogout();
|
||||
|
||||
// Add activity listeners
|
||||
document.addEventListener('click', updateActivity);
|
||||
document.addEventListener('keypress', updateActivity);
|
||||
} else {
|
||||
document.getElementById('space-result').textContent = `Error creating space: ${result}`;
|
||||
}
|
||||
} catch (e) {
|
||||
document.getElementById('space-result').textContent = `Error: ${e}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Logout from current space
|
||||
function performLogout() {
|
||||
logout();
|
||||
isLoggedIn = false;
|
||||
currentSpace = null;
|
||||
selectedKeypair = null;
|
||||
updateLoginUI();
|
||||
clearKeypairsList();
|
||||
document.getElementById('space-result').textContent = 'Logged out successfully';
|
||||
|
||||
// Clear auto-logout
|
||||
clearAutoLogout();
|
||||
|
||||
// Remove activity listeners
|
||||
document.removeEventListener('click', updateActivity);
|
||||
document.removeEventListener('keypress', updateActivity);
|
||||
}
|
||||
|
||||
// Update the keypairs dropdown list
|
||||
function updateKeypairsList() {
|
||||
const selectKeypair = document.getElementById('select-keypair');
|
||||
|
||||
// Clear existing options
|
||||
while (selectKeypair.options.length > 1) {
|
||||
selectKeypair.remove(1);
|
||||
}
|
||||
|
||||
try {
|
||||
// Get keypairs list
|
||||
const keypairs = list_keypairs();
|
||||
|
||||
// Add options for each keypair
|
||||
keypairs.forEach(keypairName => {
|
||||
const option = document.createElement('option');
|
||||
option.value = keypairName;
|
||||
option.textContent = keypairName;
|
||||
selectKeypair.appendChild(option);
|
||||
});
|
||||
|
||||
// If there's a selected keypair, select it in the dropdown
|
||||
if (selectedKeypair) {
|
||||
selectKeypair.value = selectedKeypair;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error updating keypairs list:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the keypairs dropdown list
|
||||
function clearKeypairsList() {
|
||||
const selectKeypair = document.getElementById('select-keypair');
|
||||
|
||||
// Clear existing options
|
||||
while (selectKeypair.options.length > 1) {
|
||||
selectKeypair.remove(1);
|
||||
}
|
||||
|
||||
// Clear selected keypair display
|
||||
document.getElementById('selected-pubkey-display').textContent = '';
|
||||
}
|
||||
|
||||
// Create a new keypair
|
||||
async function performCreateKeypair() {
|
||||
if (!isLoggedIn) {
|
||||
document.getElementById('keypair-management-result').textContent = 'Please login first';
|
||||
return;
|
||||
}
|
||||
|
||||
const keypairName = document.getElementById('keypair-name').value.trim();
|
||||
|
||||
if (!keypairName) {
|
||||
document.getElementById('keypair-management-result').textContent = 'Please enter a keypair name';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Create new keypair
|
||||
const result = create_keypair(keypairName);
|
||||
if (result === 0) {
|
||||
document.getElementById('keypair-management-result').textContent = `Successfully created keypair "${keypairName}"`;
|
||||
|
||||
// Update keypairs list
|
||||
updateKeypairsList();
|
||||
|
||||
// Select the new keypair
|
||||
selectedKeypair = keypairName;
|
||||
document.getElementById('select-keypair').value = keypairName;
|
||||
|
||||
// Display public key
|
||||
displaySelectedKeypairPublicKey();
|
||||
|
||||
// Save the updated space to localStorage
|
||||
saveCurrentSpace();
|
||||
} else {
|
||||
document.getElementById('keypair-management-result').textContent = `Error creating keypair: ${result}`;
|
||||
}
|
||||
} catch (e) {
|
||||
document.getElementById('keypair-management-result').textContent = `Error: ${e}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Select a keypair
|
||||
async function performSelectKeypair() {
|
||||
if (!isLoggedIn) {
|
||||
document.getElementById('keypair-management-result').textContent = 'Please login first';
|
||||
return;
|
||||
}
|
||||
|
||||
const keypairName = document.getElementById('select-keypair').value;
|
||||
|
||||
if (!keypairName) {
|
||||
document.getElementById('keypair-management-result').textContent = 'Please select a keypair';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Select keypair
|
||||
const result = select_keypair(keypairName);
|
||||
if (result === 0) {
|
||||
selectedKeypair = keypairName;
|
||||
document.getElementById('keypair-management-result').textContent = `Selected keypair "${keypairName}"`;
|
||||
|
||||
// Display public key
|
||||
displaySelectedKeypairPublicKey();
|
||||
} else {
|
||||
document.getElementById('keypair-management-result').textContent = `Error selecting keypair: ${result}`;
|
||||
}
|
||||
} catch (e) {
|
||||
document.getElementById('keypair-management-result').textContent = `Error: ${e}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Display the public key of the selected keypair
|
||||
function displaySelectedKeypairPublicKey() {
|
||||
try {
|
||||
const pubKey = keypair_pub_key();
|
||||
document.getElementById('selected-pubkey-display').textContent = `Public Key: ${bufferToHex(pubKey)}`;
|
||||
} catch (e) {
|
||||
document.getElementById('selected-pubkey-display').textContent = `Error getting public key: ${e}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the current space to localStorage
|
||||
function saveCurrentSpace() {
|
||||
if (!isLoggedIn || !currentSpace) return;
|
||||
|
||||
try {
|
||||
const password = document.getElementById('space-password').value;
|
||||
const encryptedSpace = encrypt_key_space(password);
|
||||
saveSpaceToStorage(currentSpace, encryptedSpace);
|
||||
} catch (e) {
|
||||
console.error('Error saving space:', e);
|
||||
}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
// Initialize the WebAssembly module
|
||||
await init();
|
||||
|
||||
console.log('WebAssembly crypto module initialized!');
|
||||
|
||||
// Set up the keypair generation example
|
||||
document.getElementById('keypair-button').addEventListener('click', () => {
|
||||
try {
|
||||
const result = keypair_new();
|
||||
if (result === 0) {
|
||||
document.getElementById('keypair-result').textContent = 'Keypair generated successfully!';
|
||||
|
||||
// Get and display the public key
|
||||
try {
|
||||
const pubKey = keypair_pub_key();
|
||||
document.getElementById('pubkey-display').textContent = `Public Key: ${bufferToHex(pubKey)}`;
|
||||
} catch (e) {
|
||||
document.getElementById('pubkey-display').textContent = `Error getting public key: ${e}`;
|
||||
}
|
||||
} else {
|
||||
document.getElementById('keypair-result').textContent = `Error generating keypair: ${result}`;
|
||||
}
|
||||
} catch (e) {
|
||||
document.getElementById('keypair-result').textContent = `Error: ${e}`;
|
||||
}
|
||||
});
|
||||
// Set up the login/space management
|
||||
document.getElementById('login-button').addEventListener('click', performLogin);
|
||||
document.getElementById('create-space-button').addEventListener('click', performCreateSpace);
|
||||
document.getElementById('logout-button').addEventListener('click', performLogout);
|
||||
|
||||
// Set up the keypair management
|
||||
document.getElementById('create-keypair-button').addEventListener('click', performCreateKeypair);
|
||||
document.getElementById('select-keypair').addEventListener('change', performSelectKeypair);
|
||||
|
||||
// Set up the signing example
|
||||
document.getElementById('sign-button').addEventListener('click', () => {
|
||||
if (!isLoggedIn) {
|
||||
document.getElementById('signature-result').textContent = 'Please login first';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedKeypair) {
|
||||
document.getElementById('signature-result').textContent = 'Please select a keypair first';
|
||||
return;
|
||||
}
|
||||
|
||||
const message = document.getElementById('sign-message').value;
|
||||
const messageBytes = new TextEncoder().encode(message);
|
||||
|
||||
@@ -73,6 +410,16 @@ async function run() {
|
||||
|
||||
// Set up the verification example
|
||||
document.getElementById('verify-button').addEventListener('click', () => {
|
||||
if (!isLoggedIn) {
|
||||
document.getElementById('verify-result').textContent = 'Please login first';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedKeypair) {
|
||||
document.getElementById('verify-result').textContent = 'Please select a keypair first';
|
||||
return;
|
||||
}
|
||||
|
||||
const message = document.getElementById('verify-message').value;
|
||||
const messageBytes = new TextEncoder().encode(message);
|
||||
const signatureHex = document.getElementById('verify-signature').value;
|
||||
@@ -105,7 +452,6 @@ async function run() {
|
||||
const messageBytes = new TextEncoder().encode(message);
|
||||
|
||||
try {
|
||||
// New API: encrypt_symmetric only takes key and message
|
||||
const ciphertext = encrypt_symmetric(key, messageBytes);
|
||||
const ciphertextHex = bufferToHex(ciphertext);
|
||||
document.getElementById('encrypt-result').textContent = `Ciphertext: ${ciphertextHex}`;
|
||||
@@ -130,7 +476,6 @@ async function run() {
|
||||
const ciphertext = hexToBuffer(ciphertextHex);
|
||||
|
||||
try {
|
||||
// New API: decrypt_symmetric only takes key and ciphertext
|
||||
const plaintext = decrypt_symmetric(key, ciphertext);
|
||||
const decodedText = new TextDecoder().decode(plaintext);
|
||||
document.getElementById('decrypt-result').textContent = `Decrypted: ${decodedText}`;
|
||||
@@ -141,6 +486,53 @@ async function run() {
|
||||
document.getElementById('decrypt-result').textContent = `Error: ${e}`;
|
||||
}
|
||||
});
|
||||
|
||||
// Set up the password-based encryption example
|
||||
document.getElementById('password-encrypt-button').addEventListener('click', () => {
|
||||
try {
|
||||
const password = document.getElementById('password-encrypt-password').value;
|
||||
if (!password) {
|
||||
document.getElementById('password-encrypt-result').textContent = 'Please enter a password';
|
||||
return;
|
||||
}
|
||||
|
||||
const message = document.getElementById('password-encrypt-message').value;
|
||||
const messageBytes = new TextEncoder().encode(message);
|
||||
|
||||
const ciphertext = encrypt_with_password(password, messageBytes);
|
||||
const ciphertextHex = bufferToHex(ciphertext);
|
||||
document.getElementById('password-encrypt-result').textContent = `Ciphertext: ${ciphertextHex}`;
|
||||
|
||||
// Store for decryption
|
||||
document.getElementById('password-decrypt-ciphertext').value = ciphertextHex;
|
||||
document.getElementById('password-decrypt-password').value = password;
|
||||
} catch (e) {
|
||||
document.getElementById('password-encrypt-result').textContent = `Error: ${e}`;
|
||||
}
|
||||
});
|
||||
|
||||
// Set up the password-based decryption example
|
||||
document.getElementById('password-decrypt-button').addEventListener('click', () => {
|
||||
try {
|
||||
const password = document.getElementById('password-decrypt-password').value;
|
||||
if (!password) {
|
||||
document.getElementById('password-decrypt-result').textContent = 'Please enter a password';
|
||||
return;
|
||||
}
|
||||
|
||||
const ciphertextHex = document.getElementById('password-decrypt-ciphertext').value;
|
||||
const ciphertext = hexToBuffer(ciphertextHex);
|
||||
|
||||
const plaintext = decrypt_with_password(password, ciphertext);
|
||||
const decodedText = new TextDecoder().decode(plaintext);
|
||||
document.getElementById('password-decrypt-result').textContent = `Decrypted: ${decodedText}`;
|
||||
} catch (e) {
|
||||
document.getElementById('password-decrypt-result').textContent = `Error: ${e}`;
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize UI
|
||||
updateLoginUI();
|
||||
}
|
||||
|
||||
run().catch(console.error);
|
Reference in New Issue
Block a user