Skip to content

feat(core): Add encryptWithKey and decryptWithKey to Cipher service#28608

Merged
guillaumejacquart merged 1 commit intomasterfrom
iam-492-cipher-service-accept-explicit-key-material
Apr 22, 2026
Merged

feat(core): Add encryptWithKey and decryptWithKey to Cipher service#28608
guillaumejacquart merged 1 commit intomasterfrom
iam-492-cipher-service-accept-explicit-key-material

Conversation

@guillaumejacquart
Copy link
Copy Markdown
Contributor

@guillaumejacquart guillaumejacquart commented Apr 17, 2026

Summary

Adds encryptWithKey(data, key, algorithm) and decryptWithKey(data, key, algorithm) to the Cipher service in packages/core. These let callers pass pre-resolved key material and an algorithm explicitly, instead of relying on the hardcoded InstanceSettings encryption key — unblocking call sites that resolve keys per row via KeyManagerService.

The legacy encrypt/decrypt methods are preserved and now delegate to the new ones, so there's a single CBC implementation and no regression for existing callers. The aes-256-gcm branch is stubbed and throws until the GCM implementation lands in a later ticket.

How to test: pnpm --filter n8n-core test cipher — 12 tests pass, including roundtrip, random-IV, wrong-key failure, GCM throws on both directions, and cross-compat between legacy encrypt and new decryptWithKey.

Related Linear tickets, Github issues, and Community forum posts

https://linear.app/n8n/issue/IAM-492

Review / Merge checklist

  • I have seen this code, I have run this code, and I take responsibility for this code.
  • PR title and summary are descriptive. (conventions)
  • Docs updated or follow-up ticket created.
  • Tests included.
  • PR Labeled with Backport to Beta, Backport to Stable, or Backport to v1 (if the PR is an urgent fix that needs to be backported)

🤖 PR Summary generated by AI

Adds explicit-key methods to the Cipher service so callers can pass
pre-resolved key material and an algorithm. Legacy encrypt/decrypt now
delegate to the new methods to keep a single CBC implementation. GCM
branch is stubbed and throws until the algorithm lands.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@guillaumejacquart guillaumejacquart marked this pull request as ready for review April 17, 2026 09:25
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Architecture diagram
sequenceDiagram
    participant Caller as Caller (e.g. KeyManager)
    participant Cipher as Cipher Service
    participant IS as InstanceSettings
    participant Crypto as Node.js Crypto

    Note over Caller, Crypto: Encryption Flow

    alt Legacy Path: encrypt(data, customKey?)
        Caller->>Cipher: encrypt(data, optionalKey)
        opt No customKey provided
            Cipher->>IS: Get encryptionKey (global)
            IS-->>Cipher: globalKey
        end
        Cipher->>Cipher: CHANGED: delegate to encryptWithKey()
    else NEW: encryptWithKey(data, key, algorithm)
        Caller->>Cipher: encryptWithKey(data, key, algorithm)
    end

    alt NEW: algorithm == 'aes-256-gcm'
        Cipher-->>Caller: Throw "GCM not yet implemented"
    else algorithm == 'aes-256-cbc'
        Cipher->>Crypto: randomBytes(8) (salt)
        Cipher->>Cipher: getKeyAndIv(salt, key)
        Cipher->>Crypto: createCipheriv('aes-256-cbc', derivedKey, iv)
        Cipher-->>Caller: Base64 string (Header + Salt + Ciphertext)
    end

    Note over Caller, Crypto: Decryption Flow

    alt Legacy Path: decrypt(data, customKey?)
        Caller->>Cipher: decrypt(data, optionalKey)
        opt No customKey provided
            Cipher->>IS: Get encryptionKey (global)
            IS-->>Cipher: globalKey
        end
        Cipher->>Cipher: CHANGED: delegate to decryptWithKey()
    else NEW: decryptWithKey(data, key, algorithm)
        Caller->>Cipher: decryptWithKey(data, key, algorithm)
    end

    alt NEW: algorithm == 'aes-256-gcm'
        Cipher-->>Caller: Throw "GCM not yet implemented"
    else algorithm == 'aes-256-cbc'
        Cipher->>Cipher: Extract salt from Base64 input
        Cipher->>Cipher: getKeyAndIv(salt, key)
        Cipher->>Crypto: createDecipheriv('aes-256-cbc', derivedKey, iv)
        Cipher-->>Caller: Plaintext string
    end
Loading

@guillaumejacquart guillaumejacquart requested review from a team, afitzek, cstuncsik and phyllis-noester and removed request for a team April 20, 2026 08:45
Copy link
Copy Markdown
Contributor

@afitzek afitzek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@guillaumejacquart guillaumejacquart added this pull request to the merge queue Apr 22, 2026
@n8n-assistant n8n-assistant Bot added n8n team Authored by the n8n team core Enhancement outside /nodes-base and /editor-ui labels Apr 22, 2026
Merged via the queue into master with commit c40599b Apr 22, 2026
76 of 77 checks passed
@guillaumejacquart guillaumejacquart deleted the iam-492-cipher-service-accept-explicit-key-material branch April 22, 2026 10:28
@n8n-assistant n8n-assistant Bot mentioned this pull request Apr 28, 2026
@n8n-assistant
Copy link
Copy Markdown
Contributor

n8n-assistant Bot commented Apr 28, 2026

Got released with n8n@2.19.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team Released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants