Skip to content

Commit 42fb434

Browse files
waleedlatif1claudelakeesivicecrasher321Sg312
authored
fix(encryption): specify authTagLength on all AES-GCM cipher/decipher calls (#3883)
* fix: specify authTagLength in AES-GCM decipheriv calls Fixes missing authTagLength parameter in createDecipheriv calls using AES-256-GCM mode. Without explicit tag length specification, the application may be tricked into accepting shorter authentication tags, potentially allowing ciphertext spoofing. CWE-310: Cryptographic Issues (gcm-no-tag-length) * fix: specify authTagLength on createCipheriv calls for AES-GCM consistency Complements #3881 by adding explicit authTagLength: 16 to the encrypt side as well, ensuring both cipher and decipher specify the tag length. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: clean up crypto modules - Fix error: any → error: unknown with proper type guard in encryption.ts - Eliminate duplicate iv.toString('hex') calls in both encrypt functions - Remove redundant string split in decryptApiKey (was splitting twice) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * new turborepo version --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com> Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai> Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com> Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com> Co-authored-by: NLmejiro <kuroda.k1021@gmail.com>
1 parent dcebe3a commit 42fb434

File tree

5 files changed

+30
-25
lines changed

5 files changed

+30
-25
lines changed

apps/sim/lib/api-key/crypto.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,17 @@ export async function encryptApiKey(apiKey: string): Promise<{ encrypted: string
3636
}
3737

3838
const iv = randomBytes(16)
39-
const cipher = createCipheriv('aes-256-gcm', key, iv)
39+
const cipher = createCipheriv('aes-256-gcm', key, iv, { authTagLength: 16 })
4040
let encrypted = cipher.update(apiKey, 'utf8', 'hex')
4141
encrypted += cipher.final('hex')
4242

4343
const authTag = cipher.getAuthTag()
44+
const ivHex = iv.toString('hex')
4445

4546
// Format: iv:encrypted:authTag
4647
return {
47-
encrypted: `${iv.toString('hex')}:${encrypted}:${authTag.toString('hex')}`,
48-
iv: iv.toString('hex'),
48+
encrypted: `${ivHex}:${encrypted}:${authTag.toString('hex')}`,
49+
iv: ivHex,
4950
}
5051
}
5152

@@ -55,8 +56,10 @@ export async function encryptApiKey(apiKey: string): Promise<{ encrypted: string
5556
* @returns A promise that resolves to an object containing the decrypted API key
5657
*/
5758
export async function decryptApiKey(encryptedValue: string): Promise<{ decrypted: string }> {
59+
const parts = encryptedValue.split(':')
60+
5861
// Check if this is actually encrypted (contains colons)
59-
if (!encryptedValue.includes(':') || encryptedValue.split(':').length !== 3) {
62+
if (parts.length !== 3) {
6063
// This is a plain text key, return as-is
6164
return { decrypted: encryptedValue }
6265
}
@@ -68,10 +71,9 @@ export async function decryptApiKey(encryptedValue: string): Promise<{ decrypted
6871
return { decrypted: encryptedValue }
6972
}
7073

71-
const parts = encryptedValue.split(':')
7274
const ivHex = parts[0]
73-
const authTagHex = parts[parts.length - 1]
74-
const encrypted = parts.slice(1, -1).join(':')
75+
const authTagHex = parts[2]
76+
const encrypted = parts[1]
7577

7678
if (!ivHex || !encrypted || !authTagHex) {
7779
throw new Error('Invalid encrypted API key format. Expected "iv:encrypted:authTag"')
@@ -81,7 +83,7 @@ export async function decryptApiKey(encryptedValue: string): Promise<{ decrypted
8183
const authTag = Buffer.from(authTagHex, 'hex')
8284

8385
try {
84-
const decipher = createDecipheriv('aes-256-gcm', key, iv)
86+
const decipher = createDecipheriv('aes-256-gcm', key, iv, { authTagLength: 16 })
8587
decipher.setAuthTag(authTag)
8688

8789
let decrypted = decipher.update(encrypted, 'hex', 'utf8')

apps/sim/lib/core/security/encryption.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,17 @@ export async function encryptSecret(secret: string): Promise<{ encrypted: string
2121
const iv = randomBytes(16)
2222
const key = getEncryptionKey()
2323

24-
const cipher = createCipheriv('aes-256-gcm', key, iv)
24+
const cipher = createCipheriv('aes-256-gcm', key, iv, { authTagLength: 16 })
2525
let encrypted = cipher.update(secret, 'utf8', 'hex')
2626
encrypted += cipher.final('hex')
2727

2828
const authTag = cipher.getAuthTag()
29+
const ivHex = iv.toString('hex')
2930

3031
// Format: iv:encrypted:authTag
3132
return {
32-
encrypted: `${iv.toString('hex')}:${encrypted}:${authTag.toString('hex')}`,
33-
iv: iv.toString('hex'),
33+
encrypted: `${ivHex}:${encrypted}:${authTag.toString('hex')}`,
34+
iv: ivHex,
3435
}
3536
}
3637

@@ -54,15 +55,17 @@ export async function decryptSecret(encryptedValue: string): Promise<{ decrypted
5455
const authTag = Buffer.from(authTagHex, 'hex')
5556

5657
try {
57-
const decipher = createDecipheriv('aes-256-gcm', key, iv)
58+
const decipher = createDecipheriv('aes-256-gcm', key, iv, { authTagLength: 16 })
5859
decipher.setAuthTag(authTag)
5960

6061
let decrypted = decipher.update(encrypted, 'hex', 'utf8')
6162
decrypted += decipher.final('utf8')
6263

6364
return { decrypted }
64-
} catch (error: any) {
65-
logger.error('Decryption error:', { error: error.message })
65+
} catch (error: unknown) {
66+
logger.error('Decryption error:', {
67+
error: error instanceof Error ? error.message : 'Unknown error',
68+
})
6669
throw error
6770
}
6871
}

bun.lock

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"glob": "13.0.0",
4040
"husky": "9.1.7",
4141
"lint-staged": "16.0.0",
42-
"turbo": "2.9.1"
42+
"turbo": "2.9.3"
4343
},
4444
"lint-staged": {
4545
"*.{js,jsx,ts,tsx,json,css,scss}": [

packages/db/scripts/migrate-block-api-keys-to-byok.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ function getEncryptionKeyBuffer(): Buffer {
125125
async function encryptSecret(secret: string): Promise<string> {
126126
const iv = randomBytes(16)
127127
const key = getEncryptionKeyBuffer()
128-
const cipher = createCipheriv('aes-256-gcm', key, iv)
128+
const cipher = createCipheriv('aes-256-gcm', key, iv, { authTagLength: 16 })
129129
let encrypted = cipher.update(secret, 'utf8', 'hex')
130130
encrypted += cipher.final('hex')
131131
const authTag = cipher.getAuthTag()
@@ -146,7 +146,7 @@ async function decryptSecret(encryptedValue: string): Promise<string> {
146146
const iv = Buffer.from(ivHex, 'hex')
147147
const authTag = Buffer.from(authTagHex, 'hex')
148148

149-
const decipher = createDecipheriv('aes-256-gcm', key, iv)
149+
const decipher = createDecipheriv('aes-256-gcm', key, iv, { authTagLength: 16 })
150150
decipher.setAuthTag(authTag)
151151

152152
let decrypted = decipher.update(encrypted, 'hex', 'utf8')

0 commit comments

Comments
 (0)