Uh oh!
There was an error while loading. Please reload this page.
feat(sdk-api): SJCL-free v1 decrypt with temp fallback - #9508
Open
pranavjain97 wants to merge 2 commits into
Open
feat(sdk-api): SJCL-free v1 decrypt with temp fallback#9508pranavjain97 wants to merge 2 commits into
pranavjain97 wants to merge 2 commits into
Conversation
Contributor
pranavjain97force-pushed
the
pranavjain/wcn-43-remove-sjcl-decrypt-pr1
branch
from
August 14, 2026 18:45
72f280a to
9c43b24Comparepranavjain97
marked this pull request as ready for review
August 14, 2026 18:49
pranavjain97force-pushed
the
pranavjain/wcn-43-remove-sjcl-decrypt-pr1
branch
from
August 14, 2026 19:14
9c43b24 to
7cd08edComparedanielpeng1
requested changes
Aug 14, 2026
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Comment on lines
+85
to
+92
| try { | ||
| return await decryptV1(password, ciphertext); | ||
| } catch (nativeErr) { | ||
| const message = nativeErr instanceof Error ? nativeErr.message : String(nativeErr); | ||
| // eslint-disable-next-line no-console | ||
| console.warn('[bitgo-sdk] v1 native decrypt failed on well-formed envelope; using SJCL fallback:', message); | ||
| return sjcl.decrypt(password, ciphertext); | ||
| } |
Contributor
There was a problem hiding this comment.
in this try/catch a wrong password / bad auth tag still hits the sjcl fallback + console.warn? might want to rethrow actual auth failures, and only have the fallback on unexpected crypto errors.
try{returnawaitdecryptV1(password,ciphertext);}catch(nativeErr){if(isAuthFailure(nativeErr)){thrownativeErr;}constmessage=nativeErrinstanceofError ? nativeErr.message : String(nativeErr);// eslint-disable-next-line no-consoleconsole.warn('[bitgo-sdk] v1 native decrypt failed on well-formed envelope; using SJCL fallback:',message);returnsjcl.decrypt(password,ciphertext);}Introduces decryptV1, a native (SJCL-free) replacement for v1 envelope decrypt using node:crypto on the server and crypto-browserify via the existing webpack shim in browser bundles. Byte-for-byte compatible with SJCL's envelope format. Public decrypt() wraps the native path in a temporary SJCL fallback so callers are never blocked if native fails on an unexpected envelope shape. Envelope validation (parseV1Envelope) runs BEFORE the try/catch, so malformed input still throws immediately -- only crypto-level failures on well-formed envelopes fall through to sjcl.decrypt. Auth failures (wrong password, tampered ciphertext) are rethrown as-is; only unexpected native errors emit a console.warn and enter the fallback. decryptV1WithCrypto accepts an injected crypto module so the browser shim test can exercise the real decrypt code with crypto-browserify instead of duplicating the CCM logic. decryptV1WithFallback is exported and accepts an injected native fn so tests can trigger the fallback path deterministically without stub frameworks. An io-ts codec enforces an iter cap of 100k on v1 envelopes up front, before any KDF work runs. Test coverage (251 total, all passing): - 32 Node parity tests (aes-128/256, adata, UTF-8, >64KiB, 128-bit tags, 50 randomised inputs, malformed envelope rejection) - 12 browser-shim parity tests via crypto-browserify - 8 real BitGo keycard fixture assertions across both paths - 4 fallback behavior tests including a deterministic non-auth fallback test that injects a broken native fn TICKET: WCN-2079
pranavjain97force-pushed
the
pranavjain/wcn-43-remove-sjcl-decrypt-pr1
branch
from
August 14, 2026 19:43
7cd08ed to
2fab2b3Compare
danielpeng1
left a comment
Contributor
There was a problem hiding this comment.
bump the CI tests are failing
…word' BitGoAPI.decrypt() historically mapped SJCL's `"ccm: tag doesn't match"` and WebCrypto AES-GCM's `"operation failed for an operation-specific reason"` to a stable `"incorrect password"` error. Now that v1 decrypt routes through node:crypto's AES-CCM, the auth-failure surface is `"Unsupported state or unable to authenticate data"`. Downstream coin tests expect `"incorrect password"` -- add the third message to the mapping to keep the contract stable. TICKET: WCN-2079
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces a native (SJCL-free) v1 decrypt path in
@bitgo/sdk-apiusingnode:cryptoon the server andcrypto-browserifyvia webpack's shim in the browser. Publicdecrypt()routes v1 envelopes through the native path with atemporary
sjcl.decryptfallback so callers are never blocked if native fails on an unexpected envelope shape.Part of the ongoing SJCL migration. Also introduces an io-ts codec that validates v1 envelope shape and bounds params before any KDF work.
Behavior
node:crypto→ SJCL fallback +console.warnon native failureTest evidence
crypto-browserifynode:crypto, andcrypto-browserifyon user + backup keys from a throwaway testnet Solana walletTest plan