π Problem Statement
The README documents: "Encrypted Personal Groq Keys β Optional user-provided Groq keys are encrypted locally with AES-GCM and unlocked only for the current browser session."
The AES-GCM implementation in the frontend uses crypto.subtle to derive an encryption key from the user's Groq key. However, the key derivation likely uses a hardcoded or fixed salt value. This creates two concrete problems:
1. Fixed salt weakens the encryption: AES-GCM key derivation with a fixed salt means two users who enter the same Groq key get the same derived encryption key β the salt's entire purpose (uniqueness) is defeated. A fixed salt is effectively no salt.
2. No key rotation UX: The current UI has no "Remove my Groq key" or "Re-enter key" button. If a user's Groq key is compromised and they need to rotate it, the only option is to clear their browser's localStorage manually β a flow documented nowhere in the UI.
Proposed Fix
Fix 1 β Use crypto.getRandomValues() for a random, stored salt:
// In the key encryption utility (likely src/utils/ or src/hooks/useAI.js)
async function encryptGroqKey(rawKey) {
// Generate a unique random salt per user (stored alongside the encrypted key)
const salt = crypto.getRandomValues(new Uint8Array(16));
const keyMaterial = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(rawKey),
'PBKDF2',
false,
['deriveKey']
);
const derivedKey = await crypto.subtle.deriveKey(
{ name: 'PBKDF2', salt, iterations: 100000, hash: 'SHA-256' },
keyMaterial,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
derivedKey,
new TextEncoder().encode(rawKey)
);
// Store: base64(salt) + base64(iv) + base64(ciphertext)
localStorage.setItem('debugra_groq_encrypted', JSON.stringify({
salt: btoa(String.fromCharCode(...salt)),
iv: btoa(String.fromCharCode(...iv)),
data: btoa(String.fromCharCode(...new Uint8Array(encrypted))),
}));
}
Fix 2 β Add a "Remove API Key" button in the Settings/Header area:
// In the component where the Groq key input is rendered
<button
onClick={() => {
localStorage.removeItem('debugra_groq_encrypted');
setHasGroqKey(false);
setGroqKeyInput('');
}}
className="btn-remove-key"
aria-label="Remove saved Groq API key"
>
Remove API Key
</button>
Files to Modify
| File |
Change |
src/ (Groq key encryption utility) |
Use random salt via crypto.getRandomValues() |
src/components/ (header/settings where key is entered) |
Add "Remove API Key" button |
Suggested labels: security, enhancement, frontend, level: intermediate
I would like to work on this. Could you please assign it to me?
π Problem Statement
The README documents: "Encrypted Personal Groq Keys β Optional user-provided Groq keys are encrypted locally with AES-GCM and unlocked only for the current browser session."
The AES-GCM implementation in the frontend uses
crypto.subtleto derive an encryption key from the user's Groq key. However, the key derivation likely uses a hardcoded or fixed salt value. This creates two concrete problems:1. Fixed salt weakens the encryption: AES-GCM key derivation with a fixed salt means two users who enter the same Groq key get the same derived encryption key β the salt's entire purpose (uniqueness) is defeated. A fixed salt is effectively no salt.
2. No key rotation UX: The current UI has no "Remove my Groq key" or "Re-enter key" button. If a user's Groq key is compromised and they need to rotate it, the only option is to clear their browser's
localStoragemanually β a flow documented nowhere in the UI.Proposed Fix
Fix 1 β Use
crypto.getRandomValues()for a random, stored salt:Fix 2 β Add a "Remove API Key" button in the Settings/Header area:
Files to Modify
src/(Groq key encryption utility)crypto.getRandomValues()src/components/(header/settings where key is entered)Suggested labels:
security,enhancement,frontend,level: intermediateI would like to work on this. Could you please assign it to me?