Skip to content

fix AES-GCM handling#28

Closed
AndyVale wants to merge 1 commit intoalphaonelabs:mainfrom
AndyVale:fix/AES-GCM
Closed

fix AES-GCM handling#28
AndyVale wants to merge 1 commit intoalphaonelabs:mainfrom
AndyVale:fix/AES-GCM

Conversation

@AndyVale
Copy link
Copy Markdown
Contributor

@AndyVale AndyVale commented Mar 27, 2026

Abstract

Hello, this PR has the goal to fix the _import_aes_key since the last PR seems to have broken the encryption. This addresses a critical regression in the authenticated encryption layer of our Cloudflare Python Worker, ensuring all PII and sensitive data can be successfully protected and retrieved.

Current problem

The js.crypto.subtle.importKey method (and other Web Crypto APIs) strictly requires a plain JavaScript Object for the algorithm parameter. However, by default, the Pyodide to_js() function converts Python dictionaries into JavaScript Map objects.

When a Map is passed where an Object is expected, the internal JavaScript engine fails to find the required properties (like .name), resulting in a NotSupportedError: Unrecognized key import algorithm "undefined" requested. This effectively broke all authenticated encryption paths in the application.

Fix

The solution involves three main improvements:

  1. Centralized Conversion: Introduced a lightweight jso() helper that ensures all Python-to-JS conversions for "dictionary-like" parameters use js.Object.fromEntries. This guarantees that the Web Crypto API receives plain JS Objects.
  2. Code Simplification: Moved Pyodide/JS imports to the top level and refactored the crypto helpers to use the new jso() utility. This significantly reduced the verbosity and "noise" of the to_js calls.
  3. Cleanup: Removed redundant local imports.
AES-GCM-beforeVsAfter.mp4

Fix for AES-GCM Encryption/Decryption

This PR fixes a regression in authenticated encryption handling in the Cloudflare Python Worker. The root cause was that Pyodide's to_js() function converts Python dictionaries to JavaScript Map objects, but the Web Crypto API requires plain JavaScript Objects for algorithm parameters, causing a NotSupportedError with "Unrecognized key import algorithm 'undefined'".

Key Changes

  1. New jso() helper function: Introduced a lightweight utility that wraps to_js() with dict_converter=js.Object.fromEntries to ensure Python dictionaries are converted to plain JavaScript Objects (not Map objects) that Web Crypto APIs can properly consume.

  2. Centralized imports: Moved import js and from pyodide.ffi import to_js to the top level of the module, removing redundant per-function imports.

  3. Refactored crypto functions: Updated three async functions to use the new jso() helper:

    • _import_aes_key(): Simplified key bytes, algorithm object, and usages array conversion
    • encrypt_aes(): Cleaned up IV generation, algorithm object, and plaintext buffer handling
    • decrypt_aes(): Simplified algorithm object and ciphertext buffer conversion

Impact

The changes restore correct AES-GCM encryption and decryption functionality for protecting and retrieving PII and sensitive data in the authenticated encryption layer. The fix maintains backward compatibility with legacy XOR decryption while ensuring Web Crypto receives properly formatted parameters.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

Walkthrough

A shared jso() helper function was introduced in src/worker.py to wrap pyodide.ffi.to_js with a dict_converter. This helper replaces inline to_js() calls in the AES-GCM cryptographic functions (_import_aes_key, encrypt_aes, decrypt_aes), standardizing JavaScript object conversion for Web Crypto API interactions.

Changes

Cohort / File(s) Summary
AES-WebCrypto Refactoring
src/worker.py
Introduced new jso() helper function for JS object conversion; updated _import_aes_key, encrypt_aes, and decrypt_aes functions to use the helper instead of inline pyodide.ffi.to_js calls, eliminating repeated imports and standardizing parameter handling for Web Crypto API calls.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix AES-GCM handling' directly describes the main purpose of the changeset, which is to fix AES-GCM encryption/decryption regression by introducing a jso() helper for proper JS object conversion.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/worker.py`:
- Around line 48-50: Add type hints to the jso helper to improve readability and
follow project guidelines: import typing.Any and annotate the function signature
as def jso(obj: Any) -> Any, keeping the implementation intact that calls
to_js(..., dict_converter=js.Object.fromEntries). Reference the existing jso
function and the to_js/js.Object.fromEntries usage so the maintainer knows
exactly where to add the import and signature change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c8ba5736-4e7b-4857-b67c-f30e7ce5a9b3

📥 Commits

Reviewing files that changed from the base of the PR and between d6e1a9c and 94aea6f.

📒 Files selected for processing (1)
  • src/worker.py

Comment on lines +48 to +50
def jso(obj):
"""Utility to convert Python types to JS objects (Object.fromEntries for dicts)."""
return to_js(obj, create_pyproxies=False, dict_converter=js.Object.fromEntries)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Nice fix for the Map vs Object issue!

This helper correctly addresses the root cause described in the PR — js.Object.fromEntries ensures Web Crypto APIs receive plain JS Objects instead of Maps.

One small improvement for maintainability: consider adding type hints. Since the function accepts various Python types and returns a JS proxy, something like:

from typing import Any

def jso(obj: Any) -> Any:
    ...

This helps future contributors understand the function's flexibility at a glance. As per coding guidelines, "Review Python code for... proper type hints."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/worker.py` around lines 48 - 50, Add type hints to the jso helper to
improve readability and follow project guidelines: import typing.Any and
annotate the function signature as def jso(obj: Any) -> Any, keeping the
implementation intact that calls to_js(...,
dict_converter=js.Object.fromEntries). Reference the existing jso function and
the to_js/js.Object.fromEntries usage so the maintainer knows exactly where to
add the import and signature change.

@AndyVale AndyVale closed this Mar 27, 2026
@AndyVale AndyVale reopened this Mar 27, 2026
@AndyVale
Copy link
Copy Markdown
Contributor Author

This is a duplicate of #27

@AndyVale AndyVale closed this Mar 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant