Skip to content

Commit

Permalink
feature: key integrity and expiration
Browse files Browse the repository at this point in the history
  • Loading branch information
lamorbidamacchina committed Nov 24, 2024
1 parent d6f7b25 commit 5f8a308
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,4 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
todo.txt
105 changes: 102 additions & 3 deletions public/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,71 @@ const unreadMessages = new Map();
let activeConversationWith = null;
let isViewingUsersList = window.innerWidth <= 768; // Initialize based on screen width

// Add these utility functions at the top level
const KEY_VERIFICATION = {
SECP256K1_PUBLIC_KEY_LENGTH: 130, // Length of secp256k1 public key in hex
MAX_KEY_AGE: 1000 * 60 * 60 * 24, // 24 hours in milliseconds

verifyKeyFormat(publicKeyHex) {
// Check if key is valid hex string of correct length
const isValidHex = /^[0-9a-fA-F]+$/.test(publicKeyHex);
return isValidHex && publicKeyHex.length === this.SECP256K1_PUBLIC_KEY_LENGTH;
},

verifyKeyOnCurve(publicKeyHex) {
try {
const key = ec.keyFromPublic(publicKeyHex, 'hex');
return key.validate().result;
} catch (error) {
console.error('Key validation error:', error);
return false;
}
}
};

// Add key timestamp tracking
const keyTimestamps = new Map();

function updateKeyTimestamp(userId, publicKey) {
keyTimestamps.set(userId, {
key: publicKey,
timestamp: Date.now()
});
}

function isKeyFresh(userId) {
const keyData = keyTimestamps.get(userId);
if (!keyData) return false;

const age = Date.now() - keyData.timestamp;
return age < KEY_VERIFICATION.MAX_KEY_AGE;
}

// Add this function to verify a complete key
function verifyKeyIntegrity(publicKey, userId) {
// Skip verification for own key
if (userId === socket.id) return true;

try {
// Basic format check
if (!KEY_VERIFICATION.verifyKeyFormat(publicKey)) {
console.error(`Invalid key format for user ${userId}`);
return false;
}

// Verify key is on the curve
if (!KEY_VERIFICATION.verifyKeyOnCurve(publicKey)) {
console.error(`Key validation failed for user ${userId}`);
return false;
}

return true;
} catch (error) {
console.error('Key verification error:', error);
return false;
}
}

// Connection established
socket.on('connect', () => {
console.log('Connected to server with ID:', socket.id);
Expand All @@ -30,10 +95,32 @@ socket.on('connect', () => {

// Users list update
socket.on('users-update', (users) => {
// Verify all received keys before updating local state
const validUsers = users.filter(user => {
// Check key integrity
const isValid = verifyKeyIntegrity(user.publicKey, user.socketId);

if (!isValid) {
console.warn(`Excluding user ${user.displayName} due to invalid key`);
return false;
}

// If key is valid, update its timestamp
updateKeyTimestamp(user.socketId, user.publicKey);

// Check if key hasn't expired
if (!isKeyFresh(user.socketId)) {
console.warn(`Excluding user ${user.displayName} due to stale key`);
return false;
}

return true;
});

// Update our local copy of connected users
connectedUsers = new Map(users.map(user => [user.socketId, user]));
updateUsersList(users);
updateRecipientSelect(users);
connectedUsers = new Map(validUsers.map(user => [user.socketId, user]));
updateUsersList(validUsers);
updateRecipientSelect(validUsers);
});

// Message reception
Expand Down Expand Up @@ -175,6 +262,13 @@ async function sendMessage() {

try {
const recipientPublicKey = userKeys.get(activeConversationWith);

// Verify recipient key before encrypting
if (!verifyKeyIntegrity(recipientPublicKey, activeConversationWith)) {
displayError('Cannot send message: Recipient key verification failed');
return;
}

const encryptedMessage = await encryptMessage(message, recipientPublicKey);

socket.emit('private-message', {
Expand Down Expand Up @@ -256,6 +350,11 @@ async function decryptMessage(encryptedMessage) {
// Extract components from message
const { ephemeralPublicKey, iv, encryptedData } = encryptedMessage;

// Verify ephemeral key integrity
if (!verifyKeyIntegrity(ephemeralPublicKey, 'ephemeral')) {
throw new Error('Invalid ephemeral key');
}

// Reconstruct ephemeral public key
const ephemeralKey = ec.keyFromPublic(ephemeralPublicKey, 'hex');

Expand Down

0 comments on commit 5f8a308

Please sign in to comment.