diff --git a/src/lib/crypto.ts b/src/lib/crypto.ts index ef241708..aa27fdf0 100644 --- a/src/lib/crypto.ts +++ b/src/lib/crypto.ts @@ -9,6 +9,9 @@ const IV_LENGTH = 12; const AUTH_TAG_LENGTH = 16; const KEY_ERROR_MESSAGE = "ENCRYPTION_KEY env var must be a 32-byte hex string"; +const ENCRYPTED_TOKEN_ERROR_MESSAGE = + "Encrypted token must be a hex string containing ciphertext and auth tag"; +const IV_ERROR_MESSAGE = "Token IV must be a 12-byte hex string"; function getEncryptionKey(): Buffer { const key = process.env.ENCRYPTION_KEY; @@ -46,52 +49,33 @@ export function encryptToken(plaintext: string): { }; } +export function decryptToken(encrypted: string, iv: string): string | null { + const key = getEncryptionKey(); -export function decryptToken( - encrypted: string, - iv: string -): string | null { - try { - const key = getEncryptionKey(); - - if (!/^[0-9a-fA-F]*$/.test(encrypted) || encrypted.length % 2 !== 0) { - throw new Error("Invalid encrypted token format"); - } - - if (!/^[0-9a-fA-F]*$/.test(iv) || iv.length % 2 !== 0) { - throw new Error("Invalid IV format"); - } - - const encryptedBuffer = Buffer.from(encrypted, "hex"); - const ivBuffer = Buffer.from(iv, "hex"); - - if (ivBuffer.length !== IV_LENGTH) { - throw new Error("Invalid IV length"); - } - - if (encryptedBuffer.length < AUTH_TAG_LENGTH + 1) { - throw new Error("Encrypted token too short"); - } + if (!/^[0-9a-fA-F]+$/.test(encrypted) || encrypted.length <= AUTH_TAG_LENGTH * 2) { + return null; + } - const ciphertext = encryptedBuffer.subarray( - 0, - encryptedBuffer.length - AUTH_TAG_LENGTH - ); + if (!/^[0-9a-fA-F]{24}$/.test(iv)) { + return null; + } - const authTag = encryptedBuffer.subarray( - encryptedBuffer.length - AUTH_TAG_LENGTH - ); + const encryptedBuffer = Buffer.from(encrypted, "hex"); + const ivBuffer = Buffer.from(iv, "hex"); - const decipher = createDecipheriv(ALGORITHM, key, ivBuffer); + const ciphertext = encryptedBuffer.subarray( + 0, + encryptedBuffer.length - AUTH_TAG_LENGTH + ); + const authTag = encryptedBuffer.subarray( + encryptedBuffer.length - AUTH_TAG_LENGTH + ); - decipher.setAuthTag(authTag); + const decipher = createDecipheriv(ALGORITHM, key, ivBuffer); + decipher.setAuthTag(authTag); - return Buffer.concat([ - decipher.update(ciphertext), - decipher.final(), - ]).toString("utf8"); - } catch (error) { - console.error("Token decryption failed:", error); - return null; - } + return Buffer.concat([ + decipher.update(ciphertext), + decipher.final(), + ]).toString("utf8"); } \ No newline at end of file diff --git a/src/lib/github-accounts.ts b/src/lib/github-accounts.ts index a07d848e..6238db82 100644 --- a/src/lib/github-accounts.ts +++ b/src/lib/github-accounts.ts @@ -34,7 +34,7 @@ export async function getLinkedTokens(userId: string): Promise { const rows = (data ?? []) as UserGitHubAccountRow[]; - return rows +return rows .map((row) => decryptToken(row.access_token_encrypted, row.access_token_iv) ) @@ -113,24 +113,11 @@ export async function getLinkedAccounts( const rows = (data ?? []) as UserGitHubAccountRow[]; - return rows - .map((row) => { - const token = decryptToken( - row.access_token_encrypted, - row.access_token_iv - ); - - if (!token) { - return null; - } - - return { - githubId: row.github_id ?? "", - githubLogin: row.github_login ?? "", - token, - }; - }) - .filter((account): account is LinkedAccount => account !== null); +return rows.map((row) => ({ + githubId: row.github_id ?? "", + githubLogin: row.github_login ?? "", + token: decryptToken(row.access_token_encrypted, row.access_token_iv), + })).filter((account): account is LinkedAccount => account.token !== null); } export async function getAllAccounts(