Skip to content

Commit

Permalink
Added key upload functionality
Browse files Browse the repository at this point in the history
- Exported key pairs can now be imported into your ResVault profile.
- Added verification checks for uploaded key pairs.
- Added redundancy checks for uploaded key pairs.
- UI modifications corresponding to these changes.
  • Loading branch information
apratimshukla6 committed Oct 12, 2024
1 parent e73bfad commit 2ab75da
Show file tree
Hide file tree
Showing 3 changed files with 337 additions and 55 deletions.
141 changes: 133 additions & 8 deletions src/context/GlobalContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export const GlobalProvider = ({ children }) => {
const [selectedKeyPairIndex, setSelectedKeyPairIndex] = useState(0);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [storedPassword, setStoredPassword] = useState('');

// Alert state for modals
const [alert, setAlert] = useState({ isOpen: false, message: '' });

// Function to encrypt and store key pairs
const saveKeyPairsToStorage = (keyPairs, password) => {
Expand All @@ -39,6 +42,7 @@ export const GlobalProvider = ({ children }) => {
callback(decryptedKeyPairs);
} catch (err) {
console.error('Error decrypting key pairs:', err);
setAlert({ isOpen: true, message: 'Failed to decrypt key pairs. Please check your password.' });
callback([]);
}
} else {
Expand All @@ -47,6 +51,92 @@ export const GlobalProvider = ({ children }) => {
});
};

// Function to append new key pairs while preventing duplicates and validating them
const appendKeyPairs = (newKeyPairs) => {
const password = storedPassword;
if (!password) {
console.error('Password is not available');
setAlert({ isOpen: true, message: 'Password is not available. Please log in again.' });
return;
}

// Validate each key pair
for (let i = 0; i < newKeyPairs.length; i++) {
const keyPair = newKeyPairs[i];
if (!keyPair.publicKey || !keyPair.privateKey) {
setAlert({ isOpen: true, message: `Key pair at index ${i} is missing publicKey or privateKey.` });
console.error(`Key pair at index ${i} is missing publicKey or privateKey.`);
return;
}

try {
// Decode private key from Base58
const decodedPrivateKey = Base58.decode(keyPair.privateKey);
if (decodedPrivateKey.length !== 32) {
setAlert({ isOpen: true, message: `Private key at index ${i} is not 32 bytes.` });
console.error(`Private key at index ${i} is not 32 bytes.`);
return;
}

// Derive public key from private key using nacl.sign.keyPair.fromSeed
const derivedKeyPair = nacl.sign.keyPair.fromSeed(decodedPrivateKey);
const derivedPublicKey = Base58.encode(derivedKeyPair.publicKey);

// Compare derived public key with provided public key
if (derivedPublicKey !== keyPair.publicKey) {
setAlert({ isOpen: true, message: `Public key does not match private key at index ${i}.` });
console.error(`Public key does not match private key at index ${i}.`);
return;
}
} catch (err) {
console.error('Error validating key pair:', err);
setAlert({ isOpen: true, message: `Error validating key pair at index ${i}.` });
return;
}
}

// Load existing key pairs
loadKeyPairsFromStorage(password, (existingKeyPairs) => {
// Filter out duplicates
const uniqueNewKeyPairs = newKeyPairs.filter(newKey => {
return !existingKeyPairs.some(existingKey =>
existingKey.publicKey === newKey.publicKey &&
existingKey.privateKey === newKey.privateKey
);
});

if (uniqueNewKeyPairs.length === 0) {
console.log('No new unique key pairs to add.');
setAlert({ isOpen: true, message: 'No new unique key pairs to add.' });
return;
}

const updatedKeyPairs = [...existingKeyPairs, ...uniqueNewKeyPairs];
saveKeyPairsToStorage(updatedKeyPairs, password);
setKeyPairs(updatedKeyPairs);

// Update selected key pair to the last one added
const newIndex = updatedKeyPairs.length - 1;
setSelectedKeyPairIndex(newIndex);
setPublicKey(updatedKeyPairs[newIndex].publicKey);
setPrivateKey(updatedKeyPairs[newIndex].privateKey);
saveSelectedKeyPairIndex(newIndex);

// Update 'store' with the new key pair
const encryptedPrivateKey = CryptoJS.AES.encrypt(updatedKeyPairs[newIndex].privateKey, password).toString();
const hash = CryptoJS.SHA256(password).toString(CryptoJS.enc.Hex);
const store = {
hash,
publicKey: updatedKeyPairs[newIndex].publicKey,
encryptedPrivateKey: encryptedPrivateKey,
history: [],
};
chrome.storage.sync.set({ store }, () => {
console.log('Store updated with new key pair');
});
});
};

// Function to save selected key pair index
const saveSelectedKeyPairIndex = (index) => {
chrome.storage.local.set({ selectedKeyPairIndex: index }, () => {});
Expand All @@ -65,16 +155,29 @@ export const GlobalProvider = ({ children }) => {
const password = storedPassword;
if (!password) {
console.error('Password is not available');
setAlert({ isOpen: true, message: 'Password is not available. Please log in again.' });
return;
}

const keyPair = nacl.sign.keyPair();
const newPublicKey = Base58.encode(keyPair.publicKey);
const newPrivateKey = Base58.encode(keyPair.secretKey.slice(0, 32));
const newPrivateKey = Base58.encode(keyPair.secretKey.slice(0, 32)); // Using the first 32 bytes as seed
const newKeyPair = { publicKey: newPublicKey, privateKey: newPrivateKey };

// Load existing key pairs
loadKeyPairsFromStorage(password, (existingKeyPairs) => {
// Check for duplicates before adding
const isDuplicate = existingKeyPairs.some(existingKey =>
existingKey.publicKey === newKeyPair.publicKey &&
existingKey.privateKey === newKeyPair.privateKey
);

if (isDuplicate) {
console.log('Generated key pair is a duplicate. Skipping.');
setAlert({ isOpen: true, message: 'Generated key pair is a duplicate. Skipping.' });
return;
}

const updatedKeyPairs = [...existingKeyPairs, newKeyPair];
// Save updated key pairs
saveKeyPairsToStorage(updatedKeyPairs, password);
Expand Down Expand Up @@ -109,29 +212,47 @@ export const GlobalProvider = ({ children }) => {
const password = storedPassword;
if (!password) {
console.error('Password is not available');
setAlert({ isOpen: true, message: 'Password is not available. Please log in again.' });
return;
}

loadKeyPairsFromStorage(password, (existingKeyPairs) => {
if (existingKeyPairs.length <= 1) {
console.error('Cannot delete the last remaining key pair.');
setAlert({ isOpen: true, message: 'Cannot delete the last remaining key pair.' });
return;
}

// Remove the key pair at the specified index
const updatedKeyPairs = [...existingKeyPairs];
updatedKeyPairs.splice(index, 1);

// Immediately update the key pairs state
setKeyPairs(updatedKeyPairs);

// Save the updated keyPairs back to storage
saveKeyPairsToStorage(updatedKeyPairs, password);
setKeyPairs(updatedKeyPairs);

// Reset to the first key pair after deletion
setSelectedKeyPairIndex(0);
setPublicKey(updatedKeyPairs.length > 0 ? updatedKeyPairs[0].publicKey : '');
setPrivateKey(updatedKeyPairs.length > 0 ? updatedKeyPairs[0].privateKey : '');
if (updatedKeyPairs.length > 0) {
setPublicKey(updatedKeyPairs[0].publicKey);
setPrivateKey(updatedKeyPairs[0].privateKey);
saveSelectedKeyPairIndex(0);
}

// Update 'store' with the new selected key pair
if (updatedKeyPairs.length > 0) {
const encryptedPrivateKey = CryptoJS.AES.encrypt(updatedKeyPairs[0].privateKey, password).toString();
const hash = CryptoJS.SHA256(password).toString(CryptoJS.enc.Hex);
const store = {
hash,
publicKey: updatedKeyPairs[0].publicKey,
encryptedPrivateKey: encryptedPrivateKey,
history: [],
};
chrome.storage.sync.set({ store }, () => {
console.log('Store updated with selected key pair after deletion');
});
}

// Optionally call the callback
if (callback) {
Expand Down Expand Up @@ -172,7 +293,7 @@ export const GlobalProvider = ({ children }) => {
}, []);

// Function to set selected key pair
const setSelectedKeyPair = (index) => {
const setSelectedKeyPairFn = (index) => {
if (keyPairs[index]) {
setPublicKey(keyPairs[index].publicKey);
setPrivateKey(keyPairs[index].privateKey);
Expand All @@ -182,6 +303,7 @@ export const GlobalProvider = ({ children }) => {
const password = storedPassword;
if (!password) {
console.error('Password is not available');
setAlert({ isOpen: true, message: 'Password is not available. Please log in again.' });
return;
}
const encryptedPrivateKey = CryptoJS.AES.encrypt(keyPairs[index].privateKey, password).toString();
Expand Down Expand Up @@ -216,12 +338,15 @@ export const GlobalProvider = ({ children }) => {
generateKeyPair,
selectedKeyPairIndex,
setSelectedKeyPairIndex,
setSelectedKeyPair,
setSelectedKeyPair: setSelectedKeyPairFn,
isAuthenticated,
setIsAuthenticated,
storedPassword,
setStoredPassword,
deleteKeyPair,
appendKeyPairs,
alert, // For alert modal
setAlert, // For alert modal
}}
>
{children}
Expand Down
74 changes: 68 additions & 6 deletions src/css/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -3706,33 +3706,95 @@ tr:hover {
opacity: 0.7;
}

/* Container for keypair actions */
.keypair-actions {
display: flex;
justify-content: space-between;
gap: 10px;
justify-content: center;
margin-top: 10px;
}

/* Badge Button Styles */
.badge-button {
background-color: #3c4e63; /* Matches your theme's primary color */
color: white;
border: none;
border-radius: 20px;
padding: 10px 20px;
border-radius: 50%; /* Makes the button circular */
padding: 10px; /* Uniform padding */
font-size: 14px;
cursor: pointer;
width: 48%; /* Ensures equal width for both buttons */
text-align: center;
width: 40px; /* Fixed width suitable for icons */
height: 40px; /* Fixed height suitable for icons */
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.3s;
}

.badge-button:hover {
background-color: #50647e; /* A slightly lighter shade for hover effect */
}

/* Remove margin-left from SVGs since there's no text */
.badge-button svg {
margin-left: 8px; /* Adds space between the text and icon */
margin: 0; /* Eliminates any unintended spacing */
width: 24px; /* Adjust icon size as needed */
height: 24px; /* Adjust icon size as needed */
}

/* Tooltip container */
.button-with-tooltip {
position: relative;
display: inline-block;
}

/* Tooltip text */
.tooltip-text {
visibility: hidden;
width: max-content;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 8px;
position: absolute;
z-index: 1;
bottom: 125%; /* Position above the button */
left: 50%;
transform: translateX(-50%);
opacity: 0;
transition: opacity 0.3s;
}

/* Tooltip arrow */
.tooltip-text::after {
content: "";
position: absolute;
top: 100%; /* At the bottom of the tooltip */
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: black transparent transparent transparent;
}

/* Show the tooltip text when hovering over the container */
.button-with-tooltip:hover .tooltip-text {
visibility: visible;
opacity: 1;
}

/* Additional styling for .keypair-actions to align buttons */
.keypair-actions {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 10px;
}

/* Centered icon class (optional if not needed) */
.centered-icon {
display: flex;
align-items: center;
justify-content: center;
}
Loading

0 comments on commit 2ab75da

Please sign in to comment.